Difference between revisions of "Liblicense tutorial"

From Creative Commons
Jump to: navigation, search
(Python and GTK: added the second part)
(License Chooser)
Line 112: Line 112:
  
 
[[Image:Kde_properties.png]]
 
[[Image:Kde_properties.png]]
 +
 +
=== Graphical License Chooser ===
 +
 +
I'll start with a simple example that uses the Python bindings:
 +
 +
<pre>
 +
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)
 +
chooser.get_licenses(permits=(1 << 0) | (1 << 1), requires=0, prohibits=0)
 +
>>> ['http://creativecommons.org/licenses/publicdomain/']
 +
</pre>
 +
 +
LicenseChooser takes two arguments:
 +
* The first is a string representing the jurisdiction of licenses to return.  In this case, we specify '''None''' to only return Unported licenses.  For example, "us" or "uk" would be acceptable input, for United States and United Kingdom licenses, respectively.
 +
* The second is a list of attributes to search on.  Note: this is not Creative Commons specific -- you may specify any attributes in this list.
 +
 +
Now that we have a LicenseChooser, we can query it for licenses based on flags.  There's a bit of voodoo here, but I hope that you can follow by example.  Let's start with this:
 +
 +
<pre>(1 << 0) | (1 << 1)</pre>
 +
 +
These are the '''permits''' flags from the above example.  What this says is that we want licenses that permit the 0th and 1st attribute in the '''attributes''' list -- in other words, licenses that permit Distribution and DerivativeWorks.  Flags we don't care about (in this example, '''requires''' and '''prohibits'''), we set to zero.  Alternatively, they can be left out altogether.
 +
 +
Because we also specified the Attribution and Notice attributes, the above query will return licenses that do not permit, require, or prohibit these attributes.  This particular query returns one license: Public Domain.
 +
 +
==== PyGTK Widget ====
 +
Now that we have the basics for how the LicenseChooser works, we are going to build the following  license selection widget using PyGTK:
 +
 +
[[Image:GTK_LicenseChooser.png]]
 +
  
 
=== C bindings ===
 
=== C bindings ===
 +
 +
Here is a complete, standalone example using the C bindings.  Integration into applications follows similarly to the Python bindings above.
  
 
<pre>
 
<pre>
Line 155: Line 191:
 
}
 
}
 
</pre>
 
</pre>
 
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).
 
 
<pre>
 
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))
 
</pre>
 
 
See the C bindings example for more details.
 

Revision as of 21:04, 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

Graphical License Chooser

I'll start with a simple example that uses the Python bindings:

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)
chooser.get_licenses(permits=(1 << 0) | (1 << 1), requires=0, prohibits=0)
>>> ['http://creativecommons.org/licenses/publicdomain/']

LicenseChooser takes two arguments:

  • The first is a string representing the jurisdiction of licenses to return. In this case, we specify None to only return Unported licenses. For example, "us" or "uk" would be acceptable input, for United States and United Kingdom licenses, respectively.
  • The second is a list of attributes to search on. Note: this is not Creative Commons specific -- you may specify any attributes in this list.

Now that we have a LicenseChooser, we can query it for licenses based on flags. There's a bit of voodoo here, but I hope that you can follow by example. Let's start with this:

(1 << 0) | (1 << 1)

These are the permits flags from the above example. What this says is that we want licenses that permit the 0th and 1st attribute in the attributes list -- in other words, licenses that permit Distribution and DerivativeWorks. Flags we don't care about (in this example, requires and prohibits), we set to zero. Alternatively, they can be left out altogether.

Because we also specified the Attribution and Notice attributes, the above query will return licenses that do not permit, require, or prohibit these attributes. This particular query returns one license: Public Domain.

PyGTK Widget

Now that we have the basics for how the LicenseChooser works, we are going to build the following license selection widget using PyGTK:

File:GTK LicenseChooser.png


C bindings

Here is a complete, standalone example using the C bindings. Integration into applications follows similarly to the Python bindings above.

#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;
}