Difference between revisions of "Liblicense tutorial"

From Creative Commons
Jump to: navigation, search
(License Chooser)
(Python and GTK: added the second part)
Line 40: Line 40:
 
The first check that is done makes sure that the argument is a local file by check that it begins with "file://".  Once that is confirmed, we then read the license of the file by calling liblicense.'''read'''(''filename'').  Here urllib is used to convert url encoded characters, such as %20 for a space, so that we have an accurate file path.  This is all done on everything after the first seven characters of the uri which '''get_uri'''() returned.
 
The first check that is done makes sure that the argument is a local file by check that it begins with "file://".  Once that is confirmed, we then read the license of the file by calling liblicense.'''read'''(''filename'').  Here urllib is used to convert url encoded characters, such as %20 for a space, so that we have an accurate file path.  This is all done on everything after the first seven characters of the uri which '''get_uri'''() returned.
  
After the license is read we check that it is not null.  If its not then we use liblicense.'''get_attribute''' to get the creator of the license to check if it is Creative Commons or not.  We use python's keyword '''in''' because '''get_attribute''' returns a list of values.  The arguments for '''get_attribute''' are the license uri, the desired attributes uri (the predicate in RDF terms) and finally a boolean toggling the locale awareness of the processing.  Once all of this is done we add the corresponding emblem using the FileInfo object's '''add_emblem''' function which takes the emblem keyword.  Upon install of liblicense these two icons are added.  The keyword is taken from the filename (emblem-''keyword''.{png,svg}) not the .icon file.  That explains all of the first part.  If you have further questions visit #cc on irc.freenode.net or email cc-devel@lists.ibiblio.org.  
+
After the license is read we check that it is not null.  If its not then we use liblicense.'''get_attribute''' to get the creator of the license to check if it is Creative Commons or not.  We use python's keyword '''in''' because '''get_attribute''' returns a list of values.  The arguments for '''get_attribute''' are the license uri, the desired attributes uri (the predicate in RDF terms) and finally a boolean toggling the locale awareness of the processing.  Once all of this is done we add the corresponding emblem using the FileInfo object's '''add_emblem''' function which takes the emblem keyword.  Upon install of liblicense these two icons are added.  The keyword is taken from the filename (emblem-''keyword''.{png,svg}) not the .icon file.  That explains all of the first part.  If you have further questions visit #cc on irc.freenode.net or email cc-devel@lists.ibiblio.org.
 +
 
 +
The second part of this section and of the nautilus liblicense integration is the properties tab.  Through this tab one can see the exact license the file is currently under and they can also modify the license.  This is done by utilizing the packaged LicenseChooser liblicense gtk widget.  Lets have a look.
 +
 
 +
First lets set up the nautilus API for a property page:
 +
<pre>class LicensePropertyPage(nautilus.PropertyPageProvider):
 +
    def __init__(self):
 +
        pass
 +
 
 +
    def get_property_pages(self, files):
 +
        pass
 +
</pre>
 +
Again, similar to the first section we're subclassing a nautilus class.  And again, we need to subclass one function.  This time we need to subclass the '''get_property_pages''' method.  This method takes in a list of '''FileInfo''' objects and returns a sequence of '''PropertyPage'''s.
 +
 
 +
Alright, lets get started.  While the first iteration of this integration only supported a single file, we'll jump straight to the multiple file version.
 +
<pre>def get_property_pages(self, files):
 +
        self.files = files
 +
 
 +
        self.files = filter(lambda f: f.get_uri_scheme() == 'file' and not f.is_directory(),self.files)
 +
        self.files = map(lambda f: urllib.unquote(f.get_uri()[7:]),self.files)
 +
       
 +
        if len(self.files)==0:
 +
            return
 +
</pre>
 +
In this first bit we do some python wizardry.  First we store the files list in the object itself, we'll need it later.  Now we '''filter''' out the files with a different uri scheme and those which are directories.  This leaves us with a list of '''FileInfo''' objects which have uris in the "file:///path/filename.ext" form.  So to get a list of files in the liblicense form we use '''map''' and '''urllib.unquote''' like we did in the previous part.  Finally, before we continue, we make sure we have not filtered out all of the selected items.
 +
 
 +
Now that we have a list of relevant files we need to do one of two things, if there is only one file selected we start the license chooser displaying the appropriate license or we display no license if multiple files are selected.  '''get_property_pages''' continued:
 +
<pre>self.property_label = gtk.Label('License')
 +
        self.property_label.show()
 +
       
 +
        if len(files) == 1:
 +
            license = liblicense.read(self.files[0])
 +
            if license==None:
 +
                license = liblicense.get_default()
 +
        else:
 +
            license = None
 +
        self.box = LicenseWidget(license)
 +
</pre>
 +
First, we create the label for the property tab.  After this we see how many files we are dealing with.  If its one, we call read on the first and only element of the list and proceed to check whether it exists (in other words, is ''None'' or not).  When the license is ''None'' we default to the system default license.  If there is multiple files we simply set the license to ''None''.  Finally, we create the '''LicenseWidget''' (which is a subclass of '''gtk.HBox''') passing the desired starting license uri in.
 +
 
 +
The last task we must complete is to write the license to the selected file(s) when the properties window is closed.  To do this we attach a callback to the destroy signal of the '''LicenseWidget'''.
 +
<pre>        self.box.connect("destroy",self.license_chosen)
 +
        self.box.show()
 +
       
 +
        return nautilus.PropertyPage("NautilusPython::license",
 +
                                    self.property_label, self.box),
 +
</pre>
 +
 
 +
The above should be self explanatory.  We simply use the PyGTK widget method '''connect''' to attach a method to the signal.  Then we return a sequence (note the ending comma) of '''PropertyPage'''s created in the return statement.  I copied this from the nautilus-python example and assume the arguments are an identifier, a label and a widget to be packed in the tab.
 +
 
 +
Lastly, the callback method is pretty simple.
 +
<pre>    def license_chosen(self, widget):
 +
        license = self.box.get_license()
 +
        if license:
 +
            for f in self.files:
 +
                liblicense.write(f,license)
 +
</pre>
 +
 
 +
Like all GTK callbacks the first argument is the widget which emitted the signal along with self.  Upon receiving this signal we get the current license of the widget by calling the '''LicenseWidget''''s get_license method which returns the uri for the selected license or ''None'' if no license is selected.  Then we check to see if a license was selected and if so iterate through the selected files writing the license info to each one.  This writing is done using the liblicense '''write''' method which takes the file path and the license uri.  It utilizes liblicense modules which write to a variety of different file formats.
 +
 
 +
Well, thats the end of the liblicense nautilus integration.  The entire file can be seen at [http://cctools.svn.sourceforge.net/viewvc/cctools/liblicense/trunk/src/gnome/nautilus-liblicense.py?revision=6398&view=markup  Liblicense svn browse].
 +
 
 +
--[[User:Scott Shawcroft|Scott Shawcroft]] 17:03, 29 July 2007 (EDT)
  
 
== Command-line Interface ==
 
== Command-line Interface ==

Revision as of 21:03, 29 July 2007

Python and GTK

With the release of liblicense (ll) 0.3 we've introduced the beginnings of desktop integration. With this brief guide I hope to drive further integration into other apps. While I'll be using the python bindings for simplicity, the C bindings are very similar. In this section I will show how the integration through nautilus-python is done. Here is a screenshot from nautilus.

Ll nautilus emblem.png

The first step of writing any plugin is figuring out the relevant APIs. In this case, the relevant APIs are that of liblicense and nautilus-python. The former's API is familiar to me because I wrote it but for consistencies sake the documentation is on the wiki. The nautilus-python documentation is pretty sparse as is the nautilus extension C documentation. However, I found this documentation file on the gnome svn. There are two primary integrations which I'll talk about. The first is the license emblem shown in the screenshot above. The second is a license properties tab. We'll start with the emblem because it uses the read function of liblicense.

First off to have nautilus load the plugin we place the python file in either /usr/lib/nautilus/extensions-1.0/python or ~/.nautilus/python-extensions/. Our name for the file is nautilus-liblicense.py. We'll put both aspects of the integration in this file. Next we'll import the necessary modules.

import liblicense
import urllib

import gtk
import nautilus
from liblicense.gui_gtk import *

Urllib is used to replace things such as %20 (for space) with their correct character. Gtk can be ignored for now as we'll see it in the second part. The nautilus import provides the classes which we'll subclass to provide the desired functionality. Lastly, the two liblicense imports provide us with the library functions and the gtk selector widget which we'll use later.

In order to process the files upon load we'll subclass the InfoProvider class from nautilus-python (imported as nautilus). Within this class we need to override one function, update_file_info, which takes on argument, a FileInfo object, in addition to self.

The class so far:

class LicenseInfoProvider(nautilus.InfoProvider):
    def __init__(self):
        pass
    def update_file_info(self, f):
        pass

There is nothing we need to do upon init but we'll leave it there. In a nautilus-python example they did similar. So now in the update_file_info function we'll need to read the license of the file and assign a corresponding emblem. Currently, two emblems are used, one for Creative Commons licenses and another for anything else. Here is this final code of the update_file_info function.

    def update_file_info(self, f):
        if f.get_uri()[:7]=="file://":
            license = liblicense.read(urllib.unquote(f.get_uri()[7:]))
            if license:
                if "Creative Commons" in liblicense.get_attribute(license,"http://purl.org/dc/elements/1.1/creator",False):
                    f.add_emblem("cc")
                else:
                    f.add_emblem("licensed")

The first check that is done makes sure that the argument is a local file by check that it begins with "file://". Once that is confirmed, we then read the license of the file by calling liblicense.read(filename). Here urllib is used to convert url encoded characters, such as %20 for a space, so that we have an accurate file path. This is all done on everything after the first seven characters of the uri which get_uri() returned.

After the license is read we check that it is not null. If its not then we use liblicense.get_attribute to get the creator of the license to check if it is Creative Commons or not. We use python's keyword in because get_attribute returns a list of values. The arguments for get_attribute are the license uri, the desired attributes uri (the predicate in RDF terms) and finally a boolean toggling the locale awareness of the processing. Once all of this is done we add the corresponding emblem using the FileInfo object's add_emblem function which takes the emblem keyword. Upon install of liblicense these two icons are added. The keyword is taken from the filename (emblem-keyword.{png,svg}) not the .icon file. That explains all of the first part. If you have further questions visit #cc on irc.freenode.net or email cc-devel@lists.ibiblio.org.

The second part of this section and of the nautilus liblicense integration is the properties tab. Through this tab one can see the exact license the file is currently under and they can also modify the license. This is done by utilizing the packaged LicenseChooser liblicense gtk widget. Lets have a look.

First lets set up the nautilus API for a property page:

class LicensePropertyPage(nautilus.PropertyPageProvider):
    def __init__(self):
        pass

    def get_property_pages(self, files):
        pass

Again, similar to the first section we're subclassing a nautilus class. And again, we need to subclass one function. This time we need to subclass the get_property_pages method. This method takes in a list of FileInfo objects and returns a sequence of PropertyPages.

Alright, lets get started. While the first iteration of this integration only supported a single file, we'll jump straight to the multiple file version.

def get_property_pages(self, files):
        self.files = files

        self.files = filter(lambda f: f.get_uri_scheme() == 'file' and not f.is_directory(),self.files)
        self.files = map(lambda f: urllib.unquote(f.get_uri()[7:]),self.files)
        
        if len(self.files)==0:
            return

In this first bit we do some python wizardry. First we store the files list in the object itself, we'll need it later. Now we filter out the files with a different uri scheme and those which are directories. This leaves us with a list of FileInfo objects which have uris in the "file:///path/filename.ext" form. So to get a list of files in the liblicense form we use map and urllib.unquote like we did in the previous part. Finally, before we continue, we make sure we have not filtered out all of the selected items.

Now that we have a list of relevant files we need to do one of two things, if there is only one file selected we start the license chooser displaying the appropriate license or we display no license if multiple files are selected. get_property_pages continued:

self.property_label = gtk.Label('License')
        self.property_label.show()
        
        if len(files) == 1:
            license = liblicense.read(self.files[0])
            if license==None:
                license = liblicense.get_default()
        else:
            license = None
        self.box = LicenseWidget(license)

First, we create the label for the property tab. After this we see how many files we are dealing with. If its one, we call read on the first and only element of the list and proceed to check whether it exists (in other words, is None or not). When the license is None we default to the system default license. If there is multiple files we simply set the license to None. Finally, we create the LicenseWidget (which is a subclass of gtk.HBox) passing the desired starting license uri in.

The last task we must complete is to write the license to the selected file(s) when the properties window is closed. To do this we attach a callback to the destroy signal of the LicenseWidget.

        self.box.connect("destroy",self.license_chosen)
        self.box.show()
        
        return nautilus.PropertyPage("NautilusPython::license",
                                     self.property_label, self.box),

The above should be self explanatory. We simply use the PyGTK widget method connect to attach a method to the signal. Then we return a sequence (note the ending comma) of PropertyPages created in the return statement. I copied this from the nautilus-python example and assume the arguments are an identifier, a label and a widget to be packed in the tab.

Lastly, the callback method is pretty simple.

    def license_chosen(self, widget):
        license = self.box.get_license()
        if license:
            for f in self.files:
                liblicense.write(f,license)

Like all GTK callbacks the first argument is the widget which emitted the signal along with self. Upon receiving this signal we get the current license of the widget by calling the LicenseWidget's get_license method which returns the uri for the selected license or None if no license is selected. Then we check to see if a license was selected and if so iterate through the selected files writing the license info to each one. This writing is done using the liblicense write method which takes the file path and the license uri. It utilizes liblicense modules which write to a variety of different file formats.

Well, thats the end of the liblicense nautilus integration. The entire file can be seen at Liblicense svn browse.

--Scott Shawcroft 17:03, 29 July 2007 (EDT)

Command-line Interface

Incomplete

License Chooser

Liblicense aims to make integration of license selection into applications seamless. The license chooser API allows for selecting licenses based on what the license permits, requires, and/or prohibits. For example, the license chooser API makes an excellent backend to the following:

Kde properties.png

C bindings

#include <liblicense.h>
#include <stdio.h>

int main() {
  ll_init();
  
  char *attributes[] = {
    "http://creativecommons.org/ns#Distribution",
    "http://creativecommons.org/ns#CommercialUse",
    "http://creativecommons.org/ns#DerivativeWorks",
    "http://creativecommons.org/ns#ShareAlike",
    "http://creativecommons.org/ns#Attribution",
    NULL
  };
  ll_license_chooser_t *chooser = ll_new_license_chooser(NULL,attributes);

  int permits, requires, prohibits;

  //permits Distribution(0) and DerivativeWorks(2)
  permits = (1 << 0) | (1 << 2);

  //requires ShareAlike(3) and Attribution(4)
  requires = (1 << 3) | (1 << 4);

  //prohibits CommericalUse(1)
  prohibits = (1 << 1);

  const ll_license_list_t *licenses = ll_get_licenses_from_flags(chooser,permits,requires,prohibits);
  while (licenses) {
    printf("%s\n",licenses->license);
    licenses = licenses->next;
  }

  ll_free_license_chooser(chooser);
  ll_stop();

  return 0;
}

Note how flags are specified. For example, if we want a license that requires Attribution, we need to set the 4th bit of "requires". This is because Attribution is the 4th element in the attribute array (remember, zero-based indexes here).

Python bindings

Python bindings greatly simplify the above. This retrieves all licenses that permit Distribution and Derivative Works and don't require Attribution or Notice (in other words, Public Domain).

from liblicense import LicenseChooser

attributes = ["http://creativecommons.org/ns#Distribution",
                "http://creativecommons.org/ns#DerivativeWorks",
                "http://creativecommons.org/ns#Attribution",
                "http://creativecommons.org/ns#Notice"]
chooser = LicenseChooser(None,attributes)
print chooser.get_licenses(permits=(1 << 0) | (1 << 1))

See the C bindings example for more details.