Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Thursday, May 8, 2014

Loading JavaScript configuration and i18n for Plone add-ons

collective.jsconfiguration is a Plone package that want to give a possible approach to the way of including JavaScript configuration or 18n data inside add-ons.
Although it's heavily targeted to add-ons that contains JavaScript components it will not provide any JavaScript at all, but only three possible ways to get configuration from the server.

Motivations

In last years I saw a lot of different approach to the problem, looking at the source of many different add-ons; every approach could have some advantage or side effects. I've only one golder rule: the worst you can do is to generate your JavaScript dynamically from the server:
class JavaScriptView(BrowserView):

    def __call__(self, *args, **kwargs):

        registry = queryUtility(IRegistry)
        settings = registry.forInterface(IMyPackage)

        # ...omissis...

        self.request.response.setHeader('Content-Type', 'text/javascript')
        return """function something() {
   var settings_1 = %(settings_1)s;
   var settings_2 = %(settings_2)s;

   // do something

}
""" % {"settings_1": settings.settings_1,
       "settings_2": settings.settings_2}
This approach "works", but writing something in a programming language through the usage of another is bad and ugly:
  • A developer that's look to your JavaScript code can be astonished when he get that the code in inside a Python source.
  • The more JavaScript code became complex, the more will became unreadable/unmaintainable.
  • You can't use your favorite IDE JavaScript features because he can't understand you are (partially) write a JavaScript source
  • ... (another 10 "because is bad" can be probably found) ...
 So it's clear that the JavaScript must be in a pure .js file.

A (little) step forward can be to split the code above in a "configuration part" and keep the real code (that will read configuration) in another .js file.
This is better, but still you have some JavaScript code (even if simpler) in a Python/.zpt file... and you have 2 JavaScript instead of ones.

AJAX?

Yeah, AJAX is a right answer to the problem: you write a pure JavaScript code that ask to the server, that's normally reply with a JSON, all configuration needed. You have one .js source file and nothing more.

Well... to be honest you have an additional call to the server for getting the configuration, but how this can be a bad thing? It will probably be a very small piece of data, isn't it? So very very fast...
Not exactly.
The Web is full of articles about mobile/front-end development and how to keep high performance. If you want to read something interesting about this argument take a look to the recent "Is jQuery Too Big For Mobile?".
The real enemy is not how big is your data (hey, I'm not talking of a 10Mb HTML file!) but the latency: the network (even the mobile ones) it's quite fast nowadays but the latency is always pretty high (even more on mobile). We must reduce at the minimum the loading of external resources, especially if they are resources that can't be loaded asynchronously.

Going back to our dummy example above: it's clear that the configuration is a required resource for running the something JavaScript function. We can't start using it without having server side configuration (maybe we can do a little better with some advanced approach... I find this way to call a JavaSript library before it's loaded really interesting).

It will be the same also for i18n: we can't draw a user interface before we have the internationalized strings available.
I've some experience with jarn.jsi18n, and I used it in at least a couple of "pure JavaScript" Plone projects (projects where you don't have a template where add data in other ways I'll introduce later).
In one of them I found that the user interface were loaded in english at first access; after the first attempt translated messages were used normally.
This happened because strings were loaded through an AJAX call to the server but if you use the string "too early" (before AJAX response) they were still in primer language. Luckily after the first usage the library cache translations in the browser local storage, a very smart approach indeed.
The library gives no "onload" callback, so I was planning to provide a pull request for this but... you really like the idea? Delay the execution because we need the i18n strings?!

So AJAX is bad for i18n/configuration load?

No! If you are developing a pure JavaScript application and you don't know what backend technology will be used (if any) AJAX in probably the only choice.

But I'm focused on Plone add-ons here; we know what kind of backend we have: why not load configuration directly from the Plone pages, where your JavaScript will be executed?

Load configuration and i18n from templates

When you are facing the i18n problem with JavaScript and you have a template available (so you are developing an add-on with a view, or a viewlet) you can put translation in HTML 5 Data attributes.
This way you don't need any AJAX call and (even better) you can rely on i18ndude (I tried to use i18ndude also with jarn.jsi18n but it's not fully compatible, there's no support for the "default" value of a translation msgid) and zope.i18n machinery. Cool!

The same will be for general configuration stuff: you can put you server side data inside templates, again in HTML data 5 attributes. This is not a new technique, Plone 5 added some some information inside data attributes (and for what I saw also some small encoded JSON data).

But while using template and data attributes is amazing for translation, I don't like too much using this approach for other data like server side configuration: you will probably find yourself convert a lot of strings in other data types or, if the configuration is large, flood the page of HTML 5 data attributes.

Still use JSON

What is the best way to give data to a JavaScript developer? In my opinion the simplest and most direct way is still JavaScript itself, so providing a JavaScript object.
For example: Plone 4 (and also Plone 5) gives to JavaScript developers the portal_url variable that always gives you the URL of the site. Yes, this is ugly because this variable pollute the global namespace, if another piece of code define a global portal_url var one of them will be overwritten, ... I'm also sure that will change in future, probably it will became a new data attribute, but it's still the quicker way to read that information from JavaScript.

There is an old and well know convention on how not spawn global vars all around the global namespace: put them in a data structure: instead of calling "portal_url" a "plone.portal_url" could be a lot better.
However a lot of purists neither like this approach; in facts you are still polluting the global namespace and the possibility that another JavaScript code don't define a "plone" var is near to zero... but not zero (however I still like this, and I kept it in collective.jsconfiguration).

What's left is still a JSON data source, but we don't want to use AJAX. Uhmm...

The last chance: a lot of super-power JavaScript framework started to use client side templates.
The technique is really simple: define a new script tag of a type that the browser will not know and so it will not try to execute, and put inside it what you want.

You can use this script to store demi-HTML code, or other type of data... as a plain JSON

collective.jsconfiguration

This is the way used by collective.jsconfiguration: it simply register a new viewlet (in the page head) and wait for you registration of additional configuration (from 3rd party products).

Add-ons can register three different types of them:
  • type=text/collective.jsconfiguration.json
    It will store a JSON data inside the script tag. The source will be available to be executed by JSON.parse.
  • text/collective.jsconfiguration.xml
    It will store any kind of data, but it's designed to be used with demi-HTML, as a view/page template output. As the HTML is inside a script tag you are not really forced to use an XHTML or HTML 5 data attributes but you can simply provide an XML.
  • text/javascript
    This is exactly like the JSON case, but it's for guys that like the idea to provide their data in a pure JavaScript plain object.
It is especially useful if you are developing an add-on with JavaScript but without any server side rendering element (no template, no view, no viewlet, ...) because the JavaScript will find the configuration and translation you defined in the HTML head of your page: you only need to configure what to put inside.

plone.app.registry integration

When talking of configuration and user preferences you will probably store your ones inside the Plone registry. Using collective.jsconfiguration and collective.regjsonify you can store inside your JSON or plain JavaScript object the same data stored in your add-on registry sheet.

Example application

For better understanding how collective.jsconfiguration (and collective.regjsonify) works, you can check the example application.

OT: goodbye Plone (for a while)

This was the really-last product in my list of "Plone add-ons that I think could be cool or useful" so I decided to stop spending time on Plone development for a while.
To keep myself busy I will probably start learning some new technology, or I will try to finish my Plone Workflow book...
Who knows?

Thursday, February 20, 2014

Plone, HTML 5 Canvas and Face Detection with Webcam

One of my latests articles was about HTML 5 Canvas and Webcam integration and in the same article I put all together in a Plone add-on for integrating portrait changes with Webcam.

Recently, following a retweet of one of the cool guys I follow on Twitter, I randomly hit an article that talk about a game that integrate user's webcam as a controller (unluckily I lost the original link, neither I remember the programming language used. The article were also generally introducing face recognition, and this captured my attention.
I asked myself how cool could be getting a face recognition feature with Python. It this something possible to do? Probably not so easily...

Introducing OpenCV

...or not? If you look for "face recognition" and "Python" on Google you'll always get references to OpenCV, the Open Source Computer Vision Python library.
I simply scratched the surface of this huge piece of technology as I'm totally a newbie about computer vision. What I get is that the library can really do a lot of stuff, and it's well documented.

Let me introduce some very-general information.

To use OpenCV for detecting faces on an image you must understand the difference between "face recognition" (find a know face on image or video) and "face detection" (find a face, in general). Obviously the second is a simpler task. Why? Because whatever is your task, OpenCV must be trained to find your target. For not simple task like face recognition you can (for example) train the software by providing a set of images where the object face can be found, and the result of the train is an XML file. After that you can you this file to implement something like Picasa, iPhoto or Facebook are doing when you submit new photos.

With face detection things are simpler because you can find one of those XML file online, already generated for you.

Another important informations about the library: recently a deep API changes has been performed so a lot of examples you can find online are broken (or must be fixed).

Finally, when you are able to do so, prefer the use of cv2 library instead of cv. They are more or less the same library but (for what I understand) cv2 is faster because is based on numpy, so C-compiled code.

Going back to what i did: I focused on face detection. Let see how.

Applying face detection to Plone (yes, I said it)

Meanwhile I were also fixing some minor issues in collective.takeaportrait. A new minor feature is the possibility to move the viewfinder by using mouse drag&drop (because must be forced to be in the middle of the screen is not comfortable).
Here came The Idea: how about a viewfinder that automatically center onto the face captured by the webcam?

Here my wish list:
  • JavaScript check for server side availability of face detection feature (just because OpenCV is not a simple library to be installed... and let me be honest: this is a cool feature, but not really useful)
  • With a not-so-long delay the whole Webcam image take by the canvas is sent to the server for a face detection view
  • OpenCV on the server perform the face detection
  • If a face is found, a rect is sent back to the JavaScript callback
  • The viewfinder is centered on the face using the feature already implemented for drag&drop
The use of Plone here it's a bit unnatural but It was the simplest environment for my experiment because of the work already done with the webcam in the last article. Apart Plone itself I hope you'll the general idea: how simple can be the browser/webcam/face-detection integration, whatever will be your back-end Python framework.

As you can suppose, the experiment was a success!

Introducing collective.takeaportrait face detection feature

I don't think I can add more useful details. Let's see the video!

Saturday, November 16, 2013

Dive into HTML5 Canvas

In last weeks I read a book about the new canvas element of HTML 5: HTML 5 Canvas, by Steve and Jeff Fulton
I don't want to review the book itself (just two word: is ok) but reading it lead me think about "how Canvas can change the Web in future".

First of all: I found all the HTML 5 Canvas feature interesting, but while reading the book I felt a sort of deja-vu. Every time I run one of the given example about drawing and painting, it was like I had already see it on a Web browser...
No, I'm not talking of Flash (I never had any experience with that) but about Java Applet! Yes, I really said "Java Applet"!
When I started Web development 10 years ago applet was "cool" because you were able to do a lot of stuff impossible to to with pure HTML + JavaScript. But you already know that applet failed.
Now using canvas you can do (again) a lot of drawing work, but still: how can this be useful to Web users?

HTML 5 Canvas: The Good Part

I was not able to find every answers myself, so I asked to Twitter and I get back some useful feedback..
  • Videogames development. A lot of this. In my opinion this is great! I don't have time for developing videogames anymore, but is still a discipline I like to follow. A lot of new JavaScript frameworks for game development are coming out, thanks to canvas.
  • Graphs and Histograms. We have a lot of powerful graph generator for server side languages, that can create images on the fly, but we can now stop query a remote server for that (remember that HTML 5 means also "bein offline").
  • File handling and preview. As HTML 5 is able to manage files, and canvas can also manage some type of media like images, audio and video (see below), plugins like jQuery File Upload are now possible.
I get some more good examples, but in the meantime I continued reading the book, and I found new super-cool answers myself! So: keep reading.

HTML 5 Canvas: The Super Fancy Cool Part

First of all: video!
I don't want to simply talk about HTML 5 video support or how you can control video with JavaScript, but what you can do with video and canvas.
This can be sum in a simple sentence: canvas can handle a video like it were an image. What does it means? You can draw the current video frame (taken from a standard HTML video element) inside a canvas; write the current video frame every 20 milliseconds and you are really playing the video inside it.

Why is this cool? I can already see a video element inside my HTML... so?
The great part is that you can draw a video frame inside the canvas then draw other stuff on the top of it! You can add comments and images on the running video! Wow!

And now the best part: HTML Media Capture API.
A lot of browser already support this new technology that make possible from JavaScript to access native webcam (and microphone) of the user's device. And how JavaScript can use this privileged access is not putting it into canvas?
And after that? Can I upload my work to a server? Yes!

Playing with this new toy I spent some times developing a new Plone add-on: collective.takeaportrait. This sum all the stuff I learned about media capture and video manipulation:
  • If the browser support getUserMedia call, a new button appear
  • The button will open an overlay where the webcam output is displayed
  • A viewfinder with the standard Plone ratio for user's portrait and a countdown are drawn over the streaming video
  • Use can save a photo and send it to the server for replacing the current portrait

This is something that you already see somewhere, some social networks (for what I remember, Facebook for sure) already give that chance to users, but it's really amazing to see how few lines of JavaScript code can raise the usability of your site!

Now the bad news (also reported in the Fulton & Fulton book): there's no support for those features inside mobile devices right now. Really sad.

Saturday, October 19, 2013

Reusable jQuery plugins with Bower

Inspired by the new article of Maurizio Lupo named "Reusable javascript modules with Bower" and by a recent discussion we had at work about modern front-end development (mainly focused on the Plone world), today I took some minuted to test bower.

To quickly explain bower to a Python developer I can say that it's "the Distutils of the JavaScript world".
I'm not a front-end developer because I think that "knowing how to do some JavaScript" doesn't mean being a front-end guy, however I like the direction where JavaScript is going. But note: take the rest of this article like it is: a "Bower for dummies" note!

In my last article I quickly introduced the jQuery Plugin site way of keeping update it's database: a simple JSON file. Bower is doing the same for populating its component registry site.
So an "official" jQuery plugin can be also a bower component.

Step by step

First of all you need to install the node package manager (npn), and for a MacOS user is really simple (same for Linux guys):

    $ sudo port install npm

After that you can install bower.

    $ npm install -g bower

No we'll go back to our jQuery plugin.
First of all you need the bower.json file, but instead of writing it manually, let's simply type...

    $ bower init

... and answer to the questions. After that you can go inside the file and add some other missing stuff by looking and the available syntax.

This is a possible result:
{
  "name": "waria-checkbox",
  "version": "0.2.2",
  "homepage": "http://plugins.jquery.com/waria-checkbox/",
  "description": "jQuery WAI ARIA Compatible Checkbox Plugin",
  "main": "jquery.waria-checkbox.js",
  "keywords": [
    "wai-aria",
    "accessibility",
    "js"
  ],
  "authors": [
    "Luca Fabbri <luca@keul.it>"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests",
    "CHANGES.rst",
    "README.rst",
    "demo.html",
    "LICENCE.rst",
    "README.rst",
    "waria-checkbox.jquery.json"
  ],
  "dependencies": {
    "jquery": ">=1.7"
  }
}

The bower.json file is really similar to the file needed by the jQuery plugin site, but it's not the same (a boring task: you must keep updated both files).
As we are focused on jQuery plugin, note that jQuery (that can be installed using bower) is defined in the "dependencies" section.

Finally you need to create a new git tag:

    $ git commit -am "Now is possible to install the plugin by using bower"
    $ git tag -a 0.2.2 -m "Tagging version 0.2.2"
    $ git push --all
    $ git push --tags

Last step: register the plugin onto the bower registry:

$ bower register waria-checkbox https://github.com/keul/jquery-waria-checkbox.git

Installation

    $ bower install waria-checkbox
    bower cached         git://github.com/keul/jquery-waria-checkbox.git#0.2.2
    bower validate       0.2.2 against git://github.com/keul/jquery-waria-checkbox.git
    bower cached         git://github.com/components/jquery.git#2.0.3
    bower validate       2.0.3 against git://github.com/components/jquery.git#>=1.7
    bower install        waria-checkbox#0.2.2
    bower install        jquery#2.0.3
    
    waria-checkbox#0.2.2 bower_components/waria-checkbox
    └── jquery#2.0.3
    
    jquery#2.0.3 bower_components/jquery

Now inside the bower_components folder we have both the plugin and jQuery.

Sunday, September 1, 2013

Extending jQuery selectors and facing conflicts with querySelectorAll

You know that is possible to extend the jQuery selector capabilities?
Just type...

$.expr[':'].foo = function(element) {
    ...
};
... and this function will be called when :foo selector is used.
Nothing new on this side.

Recently I started working on a new jQuery plugin and to make things simpler I needed to find a way to override an existing jQuery selector. Again: nothing new; some years ago I found that the method described in the article above can be also use for override, and not only for extend.
To make things more testable I decided to move this behavior into another (separated) jQuery plugin, which is the argument of this post.

When I used this method again nowadays I found unexpected results.
I was looking a way to change the way :checked and :checkbox selectors work, so I defined...

$.expr[':'].checkbox = function(element) {
    ...
};
...and...
$.expr[':'].checked = function(element) {
    ...
}; 
This is what I found:
  • :checkbox was working as expected
  • :checked was not working as expected
In facts, the :checked selector was only working when using it inside a .filter() call.
I'm not a jQuery core expert, I never looked at it's code very much, but this time I needed to investigate my problem. Also: I need to make this work on jQuery 1.7 and more modern 1.10 version, and codes are quite different.

Here what I found: both jQuery versions contains a method for capturing the :checked selector, but it's only called when you call filter() (so here my override attempt works as expected). For normal selectors jQuery now heavily relies on native querySelectorAll API for every browser that is supporting it.

This is the core of the problem: the :checkbox selector is a non-standard ones (not defined by any CSS specifications) while :checked is a know CSS selector. So browsers that support the :checked selector for querySelectorAll are calling this native API.

This is someway hilarious! While querySelectorAll are making our browser (and jQuery usage) faster, it's lowering jQuery extensions capabilities.

I found no smart way to change how querySelectorAll works (and probably there's no way at all, I think we are at C compiled code level here).
The trick I used is to disable the native querySelectorAll when the selector contains :checked, and in that case call my jQuery version instead.
How?

    var pattern = /\:checked/;
    if (document.querySelectorAll !== 'undefined') {
        // need to partially disable querySelectorAll for :checked
        document.nativeQuerySelectorAll = document.querySelectorAll;
        document.querySelectorAll = function(selector) {
            if (pattern.test(selector)) {
                throw('Native ":checked" selector disabled')
            }
            return this.nativeQuerySelectorAll(selector);
        }
    }

The first step is to disable (keeping a "backup") querySelectorAll, changing it with a custom function. Then all I need to do is check if the :checked selector is used somewhere in the query. If not: just call the backed up querySelectorAll, but if it's called somewhere I simply need to raise an exception.

This is the interesting part I found looking at the jQuery source: jQuery core try to use querySelectorAll every time is possible, switching to internal JavaScript code only when it's not supported. In this way I'm simulating the fact that my modern browser is not supporting :checked selector for querySelectorAll.

Problems

I found this experiment interesting, but there's some things I don't like:
  • I'm disabling the native querySelectorAll usage also when it's basic features are enough (i.e: if I really need to load only checked checkboxes and not other fancy stuff)
  • I'm wrapping every querySelectorAll calls inside the selector check, making all calls to querySelectorAll slower
  • I want only to extend jQuery here, but I'm also disabling all native querySelectorAll calls if the query containes ":checked"
  • I'm changing how JavaScript works. This is calling me back to times when I used prototype.js (that I never liked)
Any suggestion for a better code way are welcome!
This is the result of the experiment: jQuery WAI ARIA Compatible Checkbox Plugin.

Off-topic

Apart the problem described in this article, I let's spend some words on the "new" jQuery Plugin Site. Last time I published a jQuery plugin (lot of time ago) this site was a total mess: you need to authenticate, upload source tarball, write documentation on a wiki-like page, ...

I really liked how it works now: if you want to publish a plugin, just put it on GitHub and configure a commit hook! Simple and amazing!

Sunday, March 18, 2012

Views hits counter for videos in Plone

Some years ago we developed a Plone site that mainly provided video and multimedia contents. One of requirements was to obtain a views counter for videos in a classic YouTube style, but this feature at the end was cut off.
However the idea to obtain this in Plone for collective.flowplayer persisted in my mind (another product in the set of "products that I want to develop someday and probably I will never do because I don't have time").

Recently I traveled from Ferrara to Rome by train, and back forgetting the book I was reading at home, so... why don't take some time to investigate again this old feature?

Literature
First of all: what are the know ways of performing this task?
I took some time to look at the Web; unluckily I don't find some general rules or patterns. I though this not as a "visitors counter" feature: if a user visit the page where we are displaying the video player, it doesn't mean that the user will see the movie.

Now the problem: even if I don't want a perfect system and I don't care if after 100 views the counter will show 90 or 110 (the error will be the same for all views in the site, so statistically speaking, useful), I'd like to create a system where:
  • an evil visitor can spent time to raise manually it's video
  • an evil bot can't raise my counter to 10.000 in few seconds!
Let's move on, step by step on what we can do.

Monitoring the Play of my clip
Flowplayer JavaScript APIs are well done and already integrated in collective.flowplayer, so in Plone. The simplest thing we can do is to rely on the onStart clip's event. After this event has been captured by our callback we can think about sending a call to the server, that mean "the user is seeing the video".

The question: is this enough? If I provided a 5 minutes clip and my lazy visitor only see few seconds after moving away, can I count this as a new video view? Even if my super-fast Internet connection already downloaded and buffered it all?

My answer is "no" (however this can't be taken as "the right answer"). Let's try to think about a system that monitor only if the video has been totally view!

Monitor the end of my clip
The next step is to use also the onFinish event. We can monitor if the user starts to see the clip but also we can check if the clip is completed. In this way we can send to the server our message only when the video has been finished.

This is a step forward, but we can't be sure again that the user really watch the video. He could start to watch at the clip and then move the slider some seconds before the end.

Monitor cuepoints
The two JavaScript events above were already know by me, I used them while developing collective.flowplayer_toolbar.
What I learned looking at the documentation is that Flowplayer APIs also support cuepoints. Cuepoints are a set of events callback that are automatically called every n seconds (let me use 5 as n for our example).

Now we send to the server the final message only if the onStart event has been executed and if all cuepoints also are also executed. To do this we simply use a counter that is increased after every cuepoint execution.
We still rely on the onFinish event, but we can use JavaScript to be sure that the message is sent only if the counter reached an certain amount. This amount must be a value that depends on clip duration but again: Flowplayer APIs contains method to obtain video duration.

Starting to think at the Evil Guy
In a perfect world, where all are Good Guys and no one will ever try to break rules, the general description of the code given above can be enough. But we have Evil Guy.
Who is the Evil Guy? Is a technically-low-level visitor that don't know how to write code or hack JavaScript, but simply try to raise a views counter of a clip. Is a cheater.
How he can do? First of all he can spend a lot of time clicking the "Play" button again and again, every time the clip reach the end (Evil Guy always has a lot of free time).

To protect our site from this type of attack we simply need to monitor that this clip has been already view by that user. When the video starts we can send a call to the server and get from it a generated random token that we keep secretly in the JavaScript environment and on the server itself.
We will not use cookie for this, because Evil Guy can quickly learn how to manipulate them, so we choose to keep this information in the Zope session (this can lead to problems with multiple Zope instances, in that case we probably need some complex RAM cache). We store also the video path, because we want that users still able to look at other site's clip (and raise counters normally on them).

When the video reach the end we still send to the server our message but this time we also send the secret token. Only if the token match we raise the counter.
Another click of the Play button will call again the server, but this time we can see that there is already a token for that clip stored in the session. This mean that the visitor already saw that clip. This time we will stop immediately any other operation.

Even if the Evil Guy reload the browser page, he can't do anything else until the session expires.

The collective.flowplayerclipviews product
Travel from Ferrara to Rome is long, but not enough! All I described right now is more or less what you will find in the first version of collective.flowplayerclipviews. As you can imagine, I'm really far from the target!

Evil Guy gets smarter
Evil Guy can also start learning programming, and so know that JavaScript is a client side language and he can cheat, stopping the boring time he need wait for press the Play button again.
First of all: destroying the browser session is simple as close the browser or erase cookies.
After that he can simply take the secret token from the server and call the server again few milliseconds after, pretend to see the clip!

So now we can start to think about video duration server side. As we said, Flowplayer can give to you the video duration inside JavaScript environment but what about server side? There collective.flowplayer rely on the hachoir suite: inside all multimedia content Plone will store some annotations about video information (mainly: width and height).
Unluckily for us, right not collective.flowplayer is not storing also the video duration (that hachoir supports, but Plone doesn't need) so for now let simply think that collective.flowplayer will do this in the future... and we are using this future version in the rest of this article.

Having the video duration server side can be used to stop Evil Guy from raising the clip counters very quickly. Even if he write a program that take the token (simulating the Play button) and immediately call the server (simulating the clip end), we can also check that the latter request that mean "video terminated", arrives a certain amount of time later the "video started": this amount of time is the video duration!

This will be a short victory. Evil Guy can then start running hundreds of cheating Play operation contemporaneously, wait for the video duration, then send the finish message. In this way he must wait for the video duration, but he can raise the counter of hundreds/thousands anyway.

Can this be avoided? The only way is memorizing the address of the Evil Guy and keeping it in memory for some time. However ZODB is not the place for this kind of temporary data: we can think about storing this information outside, again in a RAM cache environment, or an external database, but we need to keep the write operation on ZODB at minimum.

ConclusionsAfter all those changes we can say that we have a good system... but we need to keep in mind that we can't be sure that the visitor really see our video! He can press the Play button, the go to take a shower! World is not a perfect place.

The collective.flowplayerclipviews right now is only a proof of concept, not to be used in production. If you look at the source, you can see that the _getClipDuration method is not implemented. After that all Plone feature will be there (we still need the external IP address storing structure).

During the time I spent writing this article (commonly it requires me some sessions), I tried again the search of general documentation about my argument. This time I added "plone" to the set of keyword... then I discover that we already have a Plone product that does this task (my fault: I don't googled well)!
I'm talking of collective.piwik.flowplayer! This product is part of the Plumi suite. I already checked Plumi before starting this article, but I miss that feature. Also know that this product is right now deprecated in favor of collective.piwik.mediaelement.

As many other Plumi internal submodules, those products are usable outside a whole Plumi site. Both modules are not implementing the counter feature in Plone but smartly rely on an external software: Piwik.

Piwik is an analytic software, we can say it's a competitor on Google Analytics, based on a JavaScript snippet that you must put in the page.
Apart all other analytics features, looking at the source seems that it is simply checking the Play button pressure... (let me say that I tend to complicate my own life and this is probably the good way). If this is enough for you, I strongly suggest to rely onto this service.

Conclusion (this time, really)
I hope I shown you that having this feature in Plone is possible.

Friday, January 6, 2012

Tabindex: handle focus and JavaScript events on every elements

My last reading (still in progress) is a book about JavaScript: "JavaScript Cookbook" (Shelley Powers - O'Reilly). It's full of very interesting chapters (probably it will feed many future article here).

Today I will talk about a JavaScript behavior learned in one of the examples found in the book, related to JavaScript events inside Web pages.

Types of usable event
Using pure JavaScript, not all existing events can be used on every page elements. For example: one of the first things I liked when I started using jQuery year ago was the possibility to use onclick event for whatever DOM page element, also in Internet Explorer (that commonly dosn't allow this event to be fired for all types of element).

The example I found in the book talks about another event: onkeypress.
What kind of elements can react to this kind of event? Commonly only elements able to take the focus (like links, but more common are form elements like textarea of input).
The example shows how using another uncommon JavaScript attribute we can change this behavior, enabling all other page elements. I'm talking of tabindex attribute.

How to use tabindex
As I'm mainly a Plone programmer, when tabindex was removed from all templates of the CMS (don't remember exactly, but I think it was when Plone 3.0 was released) I was happy. Why? Because tabindex is commonly not needed in a well-designed Web page.

The tabindex attribute define the order in which a user navigate the page using the keyboard (the TAB /SHIFT+TAB key) but if you define the order of your DOM elements in a logical manner you will simply not need this attribute. However note that it is not deprecated:
  • Some non-common pages can need this attribute to force the navigation first to a page section that isn't at the top (to be honest, I can't find a simple example of this)
  • A value of tabindex of "-1" make an element, commonly navigable using keyboard, not accessible anymore.
See this good tabindex reference for more details.

Now the great tabindex magic I learned: when tabindex is used on a DOM element (with a value of 0 or more), it magically gain the power of obtain focus.

Let see some examples.
Please note:
  • I tested examples with Firefox, Opera, Chrome and Safari, on MacOS. With older Internet Explorer versions you probably need some fixes in event handling.
  • If you are on MacOS, use Chrome because the keyboard navigation with TAB is a mess. See below for more on this.
The first example is a simple HTML page with 2 links, one paragraph, and where onclick and onkeypress on those elements raise simply an alert message (the text inside the node).

<html>
<head>
<script type="text/javascript">
<!--
window.onload=function() {

    var handleAction = function(event) {
        alert(this.innerHTML);
    }

    var p = document.getElementById('foo');
    p.onclick = p.onkeypress = handleAction;

    var links = document.getElementsByTagName('a');
    for (var i=0;i<links.length;i++) {
        links[i].onclick = links[i].onkeypress = handleAction;
    }

}
//-->
</script>
</head>

<body>
    <a href="javascript:;">Link 1</a>
    <p id="foo">
        Test here!
    </p>
    <a href="javascript:;">Link 2</a>
</body>
</html>
First of all the mouse event: you will be able to click on both links and the paragraph (not sure of this in all IE versions as I'm not using jQuery there).

Now let's try the keyboard navigation. Using the TAB you will be able to move only onto link elements. When one element get the focus, clicking a key you will get the same message you get for a mouse click.

If you look at the code you'll see that I'm trying to register the onkeypress event also on the HTML paragraph, but it is ignored.
This is right: a world where every DOM element can get the focus will make keyboard navigation painful. But: how can I do if I really need that a single DOM element (commonly not focusable) could take focus and keyboard events?

This is where tabindex help. If you look at the second example you will see that the only difference is the use of tabindex on the P node.
<html>
<head>
<script type="text/javascript">
<!--
window.onload=function() {

    var handleAction = function(event) {
        alert(this.innerHTML);
    }

    var p = document.getElementById('foo');
    p.onclick = p.onkeypress = handleAction;

    var links = document.getElementsByTagName('a');
    for (var i=0;i<links.length;i++) {
        links[i].onclick = links[i].onkeypress = handleAction;
    }

}
//-->
</script>
</head>

<body>
    <a href="javascript:;">Link 1</a>
    <p id="foo" tabindex="0">
        Test here!
    </p>
    <a href="javascript:;">Link 2</a>
</body>
</html>
However, the behavior difference is big.
You can try again the page with keyboard navigation with all browsers used before (again: I suggest to use Chrome if on MacOS).

Now you are able to give the focus to the paragraph! Also, keyboard event is now raised on the paragraph. You probably get the point...
Tabindex attribute can make focusable whatever element of the page and you are able to use keyboard events on them.
Isn't this great? I think that could be helpful sometimes.

Browsers differences (AKA: MacOS hate TAB)
Let's change the article focus.
Testing example one and two with all defined browsers shown some different behavior.
The tabindex definition says that element with tabindex are the first that take the focus (from the lower value to the upper), keeping the DOM order when values are equals; only then, all other focus capable elements are traversed (still, using the top-down DOM order).

For a reason I never deepen, on MacOS (but workarounds can be found on the Web) only form elements seems able to take focus (links not). Also with Firefox, that on other Linux/Windows environment works like a charm, I can't put the focus on links.
The only tested browser on MacOS that works as expected is Chrome, however also Chrome does something unexpected: if you test the second example you will see that the DOM order are kept (while the tabindexed ones must be the first).

But example 2 works also on other browsers on MacOS: you are able to put the focus using TAB key onto the paragraph.
So you can think "I can try to put a tabindex on every DOM page element I want to be accessible using keyboard to fix this MacOS problem".

If you test example 3 (that put "tabindex=0" also on A elements) you'll that this is false. Still, links are not accessible using keyboard. Strange...

Sunday, May 29, 2011

JavaScript is (like) a compiled language

False! JavaScript is not a compiled language.

However now that I get your attention, I want to introduce a programming technique that came (again, as my last post) from the great "Object-Oriented JavaScript: Create scalable, reusable high-quality JavaScript applications and libraries" (yes, I'm keep reading it).

The technique is introduced for tasks like producing functions that must be cross-browser, but the meaning behind is something more.

Function, Rewrite Thyself!

What can be the simplest way to obtain a cross-browser piece of code?
var crossBrowserMagicFunction = function() {
// some lines of code to know what browser we have
var browser = getBrowserType();
// now take some action
if (browser==='firefox') {
// do something in the Firefox way
...
} else if (browser==='chrome') {
// do something in the Google way
...
} else if (browser==='opera') {
// do something in the Opera way
...
} else if (browser==='ie') {
// do something in a crappy way
...
}
...
}
The hypothetical getBrowserType function is a example function that (maybe) perform some checks to know what browser is the one used by the visitor (who of you know the window.navigator.appCodeName can suggest that this getBrowserType function call simple this primitive code, however the "browser sniffing" is always best choice).

What's wrong with this approach? Every time we call crossBrowserMagicFunction we are repeating from scratch all the tests from knowing what kind of browser we are using.

To simplify this and perform less call to the same code we can obviously move all the code that check the browser type out of the function. You can suggest to perform this check only one time, when the page has been loaded, then save the result into a global variable (I'll use jQuery syntax for this example).
$(document).ready(function() {
// some lines of code to know what browser we have
...
BROWSER_TYPE = ...;
})
...
var crossBrowserMagicFunction = function() {
var browser = BROWSER_TYPE;
// now take some action
if (browser==='firefox') {
// do something in the Firefox way
...
} else if (browser==='chrome') {
// do something in the Google way
...
} else if (browser==='opera') {
// do something in the Opera way
...
} else if (browser==='ie') {
// do something in a crappy way
...
}
...
}
This can seems a final solution, but in last years the approach to perform "something" knowing exactly what kind of browser the visitor is using is becoming quickly obsolete.
A lot of JavaScript guides suggest you to perform instead a "browser features sniffing" operation.

Why?
Because you don't want to know what browser the visitor is using (who cares?). You want to know if (for example) the browser is able to play a music resource using HTML 5 support!
Even jQuery has deprecated old jQuery.browser features, suggesting to use instead jQuery.support. This approach also make your code compatible with future versions of a browser (a check that today works only with Firefox maybe tomorrow will work in IE 11).

What change this in our example?
Well: this invalidate the use of a global BROWSER_TYPE information. Our code can be full of functions crossBrowserMagicFuction functions, like:
  • playSound
  • playVideo
  • dragDrop
  • ...
... checks for browser features sniffing can (will) sniff for different features in different functions.

So we go back to the original code: inside the function we will perform our checks.
Now we must find a way to limit the problem of efficiency left: we need a way to perform the browser features checks only a time (at least, one time per function).

This is the way:
var crossBrowserFunction = function() {
function setup() {
... // let know what this browser is able to do
}
function doSomething () {
... // do really something info taken from setup
}
setup();
return doSomething;
}();
Done!

Just explain this a little: crossBrowserFunction is a self-invoking function, that create a JavaScript closure inside. A self-invoking function is defined and immediately called.

The two internal functions are private, but the second function doSomething are returned from the calling, so became public (note that we return doSomething, not the call of doSomething... we don't have any "()" at that line). So the result of defining crossBrowserFunction is obtaining a function that performed some check before.

In this way all the browser feature sniffing stuff is performed only one time.

Also important for me is the closure itself. Every variable defined inside the self-invoking function is available when we call later crossBrowserFunction, because in facts this is a reference to a function defined inside the closure!

For this reason I taken this title for the post.
This programming way remind me the behavior we have in a compiled language:
  • The "compile time" there is the defining an call of the self-invoking function.
  • Every use of the function obtained will be faster (no runtime check of browser features) and optimized for the current browser (platform).

Friday, April 29, 2011

Enabling a strict JavaScript logging in Firefox

I'm reading the great "Object-Oriented JavaScript: Create scalable, reusable high-quality JavaScript applications and libraries" book (to be honest, I'm finding it really better than the more know "JavaScript: The Good Parts" if you want to learn something new about JavaScript programming).

One of the great tip you read in the first pages is about how enable a better logging of warning in the JavaScript syntax in Firefox.
  • Open Firefox and go to the about:config page
  • Search the entry called javascript.options.strict
  • Set this to true
From now your Firefox will log a lot more warning in the console window (not talking of Firebug, simply the standard Firefox console). It's someway like a JSLint integrated helper!

Sunday, March 27, 2011

I video in Plone: come (e nel modo giusto)

Il Web è cambiato molto, ce ne siamo accorti? Quello che una volta era una collezione di testi collegati tra loro, oggi porta tante altre tecnologie, canali di comunicazione (media) e funzionalità. Nemmeno le immagini sono più sufficienti, e il fenomeno YouTube credo ne sia in gran parte responsabile (se così si può dire).

Se quindi i video diventano, in modo ufficiale, parte dei contenuti del Web e molto spesso il contenuto stesso, tanto vale prendere in considerazione la possibilità che il nostro CMS preferito li supporti!

NB: nel seguito ometto volutamente l'argomento HTML 5, che probabilmente nei prossimi anni semplificherà l'argomento audo/video per il Web ma ad oggi è ancora prematuro.

I video in Plone
Se la necessità è integrare un nuovo tipo di contenuto in Plone o poter vedere un video all'interno di un contenuto Plone, lo standard de facto è usare collective.flowplayer.
Questo prodotto si basa sul diffuso player open source Flowplayer e si installa facilmente in Plone, fornendo alcune funzionalità semplici ma efficaci.

Che cosa fa? In modo intelligente trasforma i file inseriti in Plone (se in uno dei formati compatibili col player) in veri e propri video riproducibili dal Web. La stessa cosa viene fatta anche con i contenuti di tipo Collegamento (ma questa seconda funzionalità la trovo di più raro utilizzo... si tratta di inserire un URL ad un file compatibile).
Altra funzionalità e permettere, previa qualche configurazione, la possibilità di inserire i video all'intero dell'editor WYSIWYG di Plone.

Credo che di articoli di blog su questo prodotto ne siano stati fatti vari, quindi non è mia intenzione soffermarmi oltre, ma prima chiariamo questo punto: ci sono alternative? Altri player video? Ovviamente sì, ma Flowplayer è il più diffuso: funziona bene e forse (ma spero in minima parte) il fatto che...
  • il creatore originale sia Martin Aspel
  • il creatore di Flowplayer sia anche il creatore delle jQuery Tools, che sono il framework per le interfacce grafiche in Plone 4
...influiscono sulla scelta.

Formati dei video
Avete mai trovato difficoltà nello spiegare ai vostri utenti come non tutti i formati video siano amici del Web? Flowplayer supporta i formati mp4 e flv. Il DivX formato AVI della comunione di vostra nipote non è propriamente un buon formato...

Nel caso quello sia l'unico formato disponibile potete usare un programma di conversione. Ma cosa fare se tutti i video che devono essere allegati partono da un formato incompatibile? Spiegare al redattore come usare un ulteriore software per fare la conversione non è mai un buon modo... la fotocamera dell'utente gli genera un file mpeg. Se non si vede su Web sembra sempre colpa di Plone!

Esiste almeno un prodotto Plone per eseguire la ri-codifica live del video. Installando collective.transcode.star avrete disponibile nel vostro buildout un nuovo demone che si occuperà di convertire i vostri video! Fantastico!

Limiti
Anche se la strada sembra spianata, ci sono alcuni limiti (o potenziali problemi) nel gestire i video in Plone.

Innanzi tutto va tenuto presente come un video sia prima di tutto un "grosso" file. Quando il browser remoto inizia la visione del video (per meglio dire, durante il buffering) il file si muove attraverso la rete. Un video in formato flv può comunque occupare centinaia di megabyte.

Questo porta a qualche problema.

Consumo della memoria
Nelle versioni di Plone precedenti alla 4.0 (non se si è installato a parte anche plone.app.blob) il caricamento da parte di Plone di un file porta anche al caricamento completo dello stesso, che viene messo nella memoria cache del thread che ha servito la richiesta. Per fortuna plone.app.blob evita questo problema (anche su Plone 3).
Anche altri prodotti come FileSystemStorage danno lo stesso beneficio, ma se potete scegliete i blob nativi di Zope.

Lock dei thread di Zope
In una installazione standard di Plone, l'application server sottostante (Zope) è configurato per gestire al massimo 4 thread concorrenti nel servire le richieste degli utenti.

Fintanto che il file intero non viene inviato, quel thread è bloccato nel servire la richiesta. Questo vale sia per l'upload del video che per la sua visualizzazione. L'aumento del numero dei thread non è una soluzione (meglio avere più istanze di zeoclient... ma dovete avere le risorse adeguate).
Questo problema è potenzialmente grave se volete usare Plone pesantemente per la gestione dei video.

Una soluzione, che scarica completamente Plone dal problema di lettura del blob e che quindi si integra con plone.app.blob introdotto sopra, è l'uso di ore.bigfile, introdotto da un ottimo articolo di Aaron VanDerlip. Questo è da tenere presente anche se non state gestendo video in particolare, ma solamente grossi file.
Per riassumere in due parole, questo prodotto sfrutta il Web server Apache (che di solito viene messo davanti ad ogni sito Plone) per lasciare completamente a lui la gestione dell'invio del video (anche in upload). Plone viene solo interrogato per check di sicurezza, poi il file blob viene preso e mandato a client che ne ha fatto richiesta.

Pare purtroppo che questo prodotto non sia rilasciato. Il grado di maturità quindi non è ben chiaro... :-(

EDIT. Da un commento, mi sono accorto che questo pezzo non è stato ben scritto. In Realtà plone.app.blob risolve anche il problema del lock del thread Zope pur non facendo nulla di speciale... Zope ha un modo efficiente di servire questo tipo di situazioni da moltissimo tempo (non ricordo, ma forse addirittura dalle versioni di Zope precedenti all'era Plone), ma per qualche motivo questo metodo non è così ben documentato e noto.

import os, stat
from ZPublisher.Iterators import filestream_iterator

def download(self):
filepath = '/home/me/VeryBigBigFile.pdf'
size = os.stat(filepath)[stat.ST_SIZE]
self.REQUEST.RESPONSE.setHeader('Content-Length', size)
return filestream_iterator(filepath, 'r')
Cosa succede? Quando nella response viene inserito un oggetto filestreamiterator, Zope sa che deve servire in modo efficiente la lettura della risorsa. Non ricordo i dettagli del funzionamento ma fondamentalmente la lettura viene gestita con un thread separato, e quello di Zope viene quindi subito liberato.

Quindi? Tutto ok?

A mio avviso questo non significa che coi BLOB abbiamo risolto tutti i problemi per rendere Plone la "Uber-Applicazione" di streaming. Come ho detto sopra, ogni istanza Zope è fatta di thread, che quindi vengono eseguiti all'interno dello stesso processo Zope (se non capite bene di cosa parlo, leggete cosa dice Wikipedia).

Un nuovo thread aumenta il lavoro totale che viene fatto dal processo. Ricordate quando sopra dicevo che l'aumento del numero di thread non è quasi mai una soluzione? Il problema è che se la vostra istanza Zope viene eseguita su una supermacchina in stile Multivac, con vari processori multicore, l'istanza (il processo) viene eseguita comunque da un processore. Più istanze (zeoclient) e uno zeoserver invece usano processi (e, se disponibili, processori) diversi.

Per tornare ai nostri blob: il thread non viene bloccato, ma il nuovo thread rende "tutto più lento".

Perché l'approccio di ore.bigfile è migliore? Perché Apache è un'applicazione che nativamente usa processi separati. Far servire il file direttamente ad Apache:
  • fa sì che il file non passi, neanche in minima parte, per l'area di memoria del processo Zope
  • non aumenta il carico dell'istanza con un nuovo thread
  • sfrutta Apache, in un processo separato.
Consumo della banda
A questo problema non c'è una soluzione pratica e spesso viene dimenticato. Il cliente vi chiede "voglio i video in Plone", ma non ci si ferma a pensare che se questi video hanno successo, è la banda disponibile al vostro sito a risentirne se decine di persone iniziano a guardare i vostri bellissimi filmati. Questo comporta che:
  • i video si vedano male (i video "scattano")
  • la navigazione del resto del sito ne risenta (il sito va lento)
  • l'attività dei redattori del sito ne risenta (il sito va lento)
Non sottovalutate mai l'opportunità di mettere i vostri video da "un'altra parte", su servizi che questo lavoro lo fanno di mestiere. Esempio?
  • YouTube
  • Vimeo
  • ...
Se un account "gratuito" su questi servizi non vi pare sufficiente, valutate anche cosa comporta spendere una piccola somma per comprare un account premium... siete certi che il gioco non valga la candela?

Video esterni: e adesso?
La cosa più semplice in assoluto è configurare Plone per consentire ai vostri redattori di inserire il codice HTML necessario per eseguire l'inclusione del video del sito remoto (teniamo come esempio principale YouTube, ma la cosa vale per qualunque altro servizio).

Dovete configurare i filtri HTML di Plone per non considerare più "cattivi" alcuni tag HTML usati per questo tipo di soluzioni: i tag sono di solito EMBED e OBJECT.

Filtri Html in PloneQuesto viene fatto dal pannello di controllo di Plone, nella sezione di gestione "Filtri HTML".

NB: c'è un motivo valido, di sicurezza, per cui Plone di base non permette agli utenti di poter usare questi tag... un redattore potrebbe inserire codice malevolo nelle vostre pagine!

Come detto precedentemente la documentazione di collective.flowplayer affronta anche questo argomento. Se siete interessati

Un altro interessante approccio piuttosto recente è un prodotto sviluppato da Quintagroup per interfacciarsi con i servizi di embed.ly.
Usando collective.embedly il codice per l'integrazione con i video remoti viene generato in un modo semplice, non dipendente dalla piattaforma scelta (e ne sono supportate varie) e usando TinyMCE! Del codice Javascript al momento del rendering della pagina "trasforma" i link marcati nel player video corretto! Questo elimina anche il problema di abilitate i tag potenzialmente pericolosi in TinyMCE!
Notevole, ma solo per Plone 4.

Ancora video esterni... ma pur sempre contenuti
Il limite di inserire video esterni nei modi descritti sopra è che il video diventa parte di un contenuto ma non è un contenuto. Non possiamo taggarlo o associargli gli utili metadati che abbiamo in Plone. Non possiamo fornire ai nostri utenti una collezione di video... questo potrebbe essere un grosso limite per la parte redazionale del vostro sito.

Quindi? L'unica soluzione è avere la banda? Non esattamente. In RedTurtle ad esempio abbiamo sviluppato un prodotto, redturtle.video, che fa di nuovo dei video un contenuto. Il prodotto segue espressamente una specifica richiesta di un cliente ed è composto da due contenuti: il tipo "Video Interno" e il "Link a Video".
Mentre il primo è tutto sommato un semplice contenuto costruito sulle funzionalità già date da collective.flowplayer (ma faccio notare una questione di usabilità non da poco: non è semplice capire che per creare un "video" in Plone un utente dovrebbe inserire un "file", come invece è nel funzionamento di collective.flowplayer... l'utente si aspetta che nel menù di aggiunta element ci sia "Video") voglio evidenziare ciò che viene dato da contenuto di tipo link.
In minima parte è, di nuovo, un contenuto costruito su qualcosa che da collective.flowplayer, ma fa anche altro: un po' come collectve.embedly si integra con siti esterni che forniscono servizi video, ma tramite un contenuto simile al link. L'utente inserisce un link a YouTube, oppure Vimeo, e di nuovo abbiamo il nostro player integrato (e tag, correlati, etc...).

Altri prodotti
Prima di cambiare rotta con un argomento parallelo, voglio fare notare come le suite di prodotti nel mondo di Plone non si limitano al solo collective.flowplayer.
Eccone altri:
  • Plumi. Praticamente trasforma Plone in un CMS per i Video. Se il vostro target è creare un sito il cui documento principale sono i video, valutate seriamente questo prodotto!
  • Plone4ArtistsVideo. Storicamente è stato il primo prodotto che si è occupato di video... funziona ancora... ma il Web è pieno di persone che si sono pentite di averlo installato.
  • Plone Video Suite. Progetto interessante e di buone intenzioni. Nasce da alcuni sprint che volevano portare ordine nel caos della situazione video in Plone (probabilmente il ruolo di Attila in questo caos lo ha fatto P4A Video :-().
    Purtroppo ad oggi non si sa a cosa porti questo progetto.
La panoramica sui prodotti video è finita, ma vi pare poco?

Per quanti riguarda l'accessibilità
Per chi come me l'accessibilità dei siti Web non è solo un'abitudine, ma una necessità quotidiana nel lavoro, sa come inizialmente i requisiti della Legge Stanca (Legge 4/2004) possano a volte sembrare limitanti in maniera piuttosto stringente. In realtà, sebbene sia una legge lungi dall'essere perfetta (e per chi ha lavorato con Plone sa bene cosa significhi il Requisito 1, relativo al codice HTML/XHTML Strict) è comunque una legge "giusta".

Un sito accessibile è quasi sempre un sito ben costruito, e non solo da un punto di vista strutturale, ma anche umano. Sapendo che il sito sviluppato ha tenuto conto, magari anche solo in minima parte, di utenti non vedenti, non udenti o ad altre forme di disabilità dovrebbe farci sentire meglio.

Ecco gli "obiettivi e finalità" della Legge:
La Repubblica riconosce e tutela il diritto di ogni persona ad accedere a tutte le fonti di informazione e ai relativi servizi, ivi compresi quelli che si articolano attraverso gli strumenti informatici e telematici.
Se per quanto detto fin'ora il nostro sforzo è che i video diventino contenuti, i video devono essere accessibili. Ci sono ovviamente requisiti tecnici specifici riguardanti i video e i contenuti multimediali nella Legge Stanca, ma chiediamoci prima di tutto come un video può essere completamente accessibile.

Quali difficoltà ci sono nell'accesso ad un video sul Web?
  • Accessibilità dei comandi del video a tutti i dispositivi di input
  • Accesso ai contenuti del video a soggetti non vedenti
  • Accesso ai contenuti del video a soggetti non udenti
  • Accesso ai contenuti del video a soggetti non udenti e non vedenti
Molto di quello che leggere di seguito viene dalla lettura dell'ottimo libro di Michele Diodati: "Accessibilità - guida completa".

Accessibilità dei dispositivi di input
Non esiste solo il mouse, anzi: fingiate esista solo la tastiera! Questa regola non vale solo per i video ovviamente, ma per i contenuti Web, Plone in proposito fa già un ottimo lavoro.
Per l'accesso al video l'attenzione si sposta sul player usato.

Le ultime versioni di Flowplayer dovrebbero aver migliorato la situazione. Uso il condizionale perché non ho avuto esperienze dirette di recente, ma ho solo letto i commenti degli autori alle ultime release.

Tempo fa, essendo rimasto piuttosto affascinato dal mondo dei plugin che girano attorno a Flowplayer, ho sviluppato un prodotto Plone, collective.flowplayer_toolbar, che sfrutta le funzionalità di uno di questi plugin: il plugin della barra di controllo.
Questo prodotto funziona bene anche con le versioni recenti di Plone e collective.flowplayer, quindi nel caso qualcosa nell'accessibilità della barra di controllo Flash non vi piaccia, potete pur sempre usare questo! :-)

Audiodescrizione dei video
E' l'argomento che conosco meno, quindi passerò oltre in fretta.

L'audiodescrizione si occupa dei soggetti non vedenti o ipovedenti. Non fermatevi a questa frase pensando che se il vostro video ha una traccia audio basti il sonoro del video stesso... questo può essere vero per alcuni tipi di filmati (mi permetto di dire a istinto che per un video contenente un'intervista non sia necessaria nessuna descrizione aggiuntiva), ma non è una regola generale.
L'audiodescrizione è un'ulteriore traccia audio che si sovrappone alla prima (ovviamente, senza inficiarne la comprensibilità) e che commenta, magari nelle pause del testo parlato, che cosa succede a video. E' l'audio per le scene dove l'audio non è sufficiente a capire cosa succede.

Non conosco le caratteristiche tecniche sul come realizzare un'audiodescrizione ma credo sia la cosa più complessa da fare per rendere un video accessibile, sia dal punto di vista del tempo investito che per la tecnologia richiesta (soprattutto se il video ha già in effetti una sua traccia audio).
Potrei sbagliare, e nel caso vi invito a correggermi.

I sottotitoli del video
Per qualche motivo più diffusi e conosciuti al grande pubblico (forse siamo tutti figli della pagina 777 di televideo), chi beneficia dei sottotitoli sono soggetti non udenti o in generale con disabilità all'udito che non permettono loro di comprendere i suoni e il parlato.

La soluzione a questo tipo di disabilità sono appunto i sottotitoli. La sottotitolazione di un video è più semplice dell'audiodescrizione, forse perché è più semplice anche da comprendere che cosa va scritto effettivamente scritto.
Le risorse sono anche più diffuse, cito ad esempio l'interessante progetto Universal Subtitles.

Tornando al nostro compagno collective.flowplayer, esiste un ulteriore plugin di Flowplayer per poter visualizzare i sottotitoli nei video: il captions plugin.
Ancora una volta la sua integrazione con Plone è stata piuttosto semplice: lo sviluppo di collective.flowplayercaptions non ha richiesto enormi sforzi se non comprendere bene le API del plugin (piuttosto confuse) e creare un'integrazione non troppo invasiva per i file Plone.

Questo prodotto non fa tutto: è comunque il redattore che deve fornire il file contenente i sottotitoli (supportato è il formato RST).

Il costo in termini tecnologici per ottenere i sottotitoli e basso, ma c'è un certo sforzo in termini di tempo.

La trascrizione testuale del video
La trascrizione video è l'alternativa dove cadono tutti quelli che vogliono tentare di rendere il proprio sito accessibile. E' già qualcosa, ma non si potrebbe fare di meglio.

Chi beneficia della trascrizione video è l'utente sordocieco, che per ovvi motivi non può trovare né beneficio dall'audiodescrizione né dai sottotitoli.
Con la completa trascrizione del contenuto del video, compresi di dialoghi e descrizioni, può invece (sempre attraverso dispositivi hardware specifici) "leggere" il testo scritto tradotto in Braille.

Dal punto di vista tecnico, Plone può fornire una trascrizione testuale ad un video tramite un semplice contenuto correlato, magari ad una semplice pagina. E' ovvio che il redattore deve inserire nel CMS questo documento, ma la sua realizzazione spaventa meno che non fornire un'audiodescrizione o un supporto ai sottotitoli. Per di più si potrebbe pensare "se con questa alternativa supero ben due disabilità, tanto vale limitarsi a questa"! Vero... ma non molto giusto.

E' ovvio che un utente non udente o un non vedente possono accedere beneficiare della trascrizione testuale, ma l'esperienza vissuta è assai limitata. A questo punto perché non rimuovere il video per tutti e fornire solo le trascrizioni?! Vi piacerebbe?

Accessibilità delle risorse esterne
Risolvendo un problema, ne introduco un altro. Possiamo sentirci tranquilli se un video preso da YouTube e inserito nel nostro sito Plone non è accessibile? Dopo tutto la colpa è di YouTube, giusto?

Questo scaricabarile non dovrebbe farci sentire meglio, e per un ente pubblico questa non può essere una scusa valida. Rendere anche i video presi da YouTube (e probabilmente da altri servizi) accessibili si può, ma richiede uno sforzo aggiuntivo.

Un documento con vari interessanti spunti è "Captioning YouTube Video and Providing Accessible Controls".

Conclusioni
I video in Plone, integrati o inseriti come contenuti, sono possibili, che sia un semplice video di pochi secondi ad un intero sito dedicato ai contenuti multimediali.

L'accessibilità dei video è una sfida reale. E' possibile, ma non nascondiamo che sia costosa in vari termini.