Le Nom de la Chose

Quel doit être le nom du CVK ?
Je ne sais pas. Il y a des gens autrement plus qualifiés que moi qui se posent la question. En attendant, on a besoin d’avancer et ce genre de détail a un impact sur le code.
Dans la version actuelle, un très grand nombre de classes sont préfixées par CVK (CVKContext par exemple). Nous avons décidé de supprimer ce préfixe.
Il reste cependant l’espace de nommage : CVK.Core, CVK.Model, etc.
Si le nom retenu finalement est Ubikey (par exemple ;-)), va-t-il falloir tout renommer en UBK ? Si oui, il faudra aussi renommer les répertoires du système de fichier (il serait très désagréable de travailler avec un tel décalage).
 
Non seulement cela demande quelques heures de travail, mais renommer des répertoires proche de la racine « casse » assez violemment le dépôt de code source sous Subversion (notamment la consultation de l’historique). (Oui, je sais, normalement cela fonctionne très bien le renommage dans SVN… J’ai néanmoins eu des très mauvaises expériences.)
 
Parallèlement à cela, en essayant de faire passer FxCop sur les sources, cette règle est apparue :
 
 Resolution   : "Correct the casing of 'CVK' in assembly name 'CVK.SharedDic.dll'
                 by changing it to 'Cvk'."
 Help         : http://msdn2.microsoft.com/library/ms182240(VS.90).aspx  (String)
 RuleFile     : Naming Rules  (String)
 Info         : "Type, namespace, and member identifiers are Pascal-cased.
                 Parameter identifiers are camel-cased. Two letter acronyms
                 within these identifiers should be upper-cased, for
                 example, use System.IO instead of System.Io. Acronyms
                 of three or more letters should be Pascal-cased, for
                 example, use System.Xml instead of System.XML. The
                 pascal-casing convention capitalizes the first letter
                 of each word, as in BackColor. The camel-casing convention
                 formats the first letter of the first word in lowercase
                 and capitalizes the first letter of all subsequent
                 words, as in backgroundColor. Although it may be common
                 practice for some two letter acronyms to not be fully
                 capitalized, violations of this rule should not be
                 excluded for this reason. For example, 'DbConnection',
                 is common but incorrect; use DBConnection. A violation
                 of this rule might be required for compatibility with
                 existing, non-managed symbol schemes. In general, however,
                 these symbols should not be visible outside the assembly
                 that uses them."

 
En substance, il faut renommer CVK en Cvk pour être parfaitement en ligne avec les règles de nommage… Ou alors, si l’on souhaite conserver les majuscules, il faut retirer une lettre…
 
Du coup, j’ai décidé de nommer (une bonne fois pour toute je l’espère) l’objet technique qu’est ce Custom Keyboard en… Custom Keyboard. Ce qui nous fait un assez satisfaisant (de mon point de vue) espace de nommage (et préfixe éventuel) CK.
 
Pour conclure, il est clair que CK : « C’est le Kode ! »
Cela ne présage nullement de la dénomination officielle du produit. Mais au moins, on peut bosser.

La licence de CiviKey

Un petit mot au sujet de la licence de CiviKey. Il s’agit d’une L-GPL. Qu’est-ce donc exactement ? Que pouvez-vous faire réellement avec les codes sources, les binaires (librairies dynamiques compilées et exécutables) ?

1. Un résumé

Ci-dessous un descriptif de licence type trouvé sur internet (http://dbaling.blogspot.com/2009/02/find-missing-index.html), comme on peut en trouver beaucoup, mais avec en sus une explication claire et concise (mise en gras ci-dessous).
Il s’agit d’une licence associée à une procédure stockée, d’où la référence à une base de données.

Copyright:
Licensed under the L-GPL - a weak copyleft license - you are permitted to use this as a component of a proprietary database and call this from proprietary software.
Copyleft lets you do anything you want except plagiarize, conceal the source, proprietarize modifications, or prohibit copying & re-distribution of this script/proc.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
see <http://www.fsf.org/licensing/licenses/lgpl.html> for the license text.

Je considère cet « extrême résumé » comme plutôt bien fait et vais donc en profiter pour le traduire et le commenter dans le cadre de CiviKey.

2. La notion de « copyleft licence »

Voir wikipedia pour le terme de copyleft. En résumé c’est un jeu de mot sur l’opposition à copyright (droit d’auteur) dont le principe est le suivant
« Je suis l’auteur de ce code. J’ai fixé des limites (ou pas) à ce que vous pouvez faire de ce code via une licence. Vous pouvez faire ce que vous voulez de mon code (dans les limites de la licence : le vendre, le distribuer gracieusement, le modifier, etc.), mais en aucun cas vous ne pouvez restreindre les libertés garanties par cette licence. »
 
Pour faire encore plus simpl(ist ?)e : la notion de Copyleft permet à un auteur gentil de mettre à disposition son œuvre en ayant la garantie qu’aucun business man méchant ne s’accapare son travail et dénie à autrui ce que l’auteur gentil l’avait gentiment autorisé à faire.
 
Ça, c’est le Copyleft en général. On en distingue deux catégories très différentes en pratique : le faible (weak) et le fort (strong).

2.1    Strong Copyleft

Toujours de façon très synthétique : le Copyleft « fort » impose à toute utilisation de l’œuvre d’être redistribuée sous la même licence, là où le « faible » n’impose sa licence que pour certaines utilisations qui touchent à l’intégrité de l’œuvre elle-même : principalement la modification ou la traduction.
 
Le Copyleft fort est souvent qualifié de « virale » : tout programme qui utilise un composant avec ce type de licence devient de facto, et dans son intégralité, soumis à cette licence. La licence GPL est de ce type et l’enjeu devient alors de distinguer les utilisations qui imposent la licence de celles qui ne l’imposent pas.
Ici, on entre dans des détails techniques auxquels d’une part les juristes ne comprennent pas grand-chose (à l’instar de certains « informaticiens » d’ailleurs) et d’autre part qui évoluent avec les technologies : liens statiques ou dynamiques, encapsulation d’un exécutable, utilisation de la spécialisation (héritage), etc.
En résumé : l’objectif d’un Copyleft fort est de dire aux autres informaticiens « Mon composant vous plaît ? Si vous l’utilisez, vous devez jouer le jeu de l’Open Source comme moi ! Sinon, débrouillez vous autrement. »

2.2 Weak Copyleft

Ce n’est pas le cas de CiviKey qui est en L-GPL. « L » pour « Lesser », c'est-à-dire plus faible. Elle est plus faible dans le sens où elle permet l’utilisation du composant logiciel sans imposer sa licence : le coté « viral » a disparu.

3. La licence de CiviKey : L-GPL

Alors que reste-t-il comme contraintes à respecter ? Très peu.

you are permitted to use this as a component of a proprietary application and call this from proprietary software.

Vous pouvez utiliser CiviKey dans une application spécifique et propriétaire (dont vous ne divulguez pas les sources) et la commercialiser, développer des plugins propriétaires (dont vous ne divulguez toujours pas les sources) et les commercialiser conjointement ou non avec leur propre licence que vous choisissez comme bon vous semble.

Copyleft lets you do anything you want except plagiarize, conceal the source, proprietarize modifications, or prohibit copying & re-distribution of this script/proc.

Vous ne pouvez pas :

  • Plagier CiviKey, c'est-à-dire copier tout ou partie du code source en l’intégrant à votre programme.
  • Dissimuler l’origine : c'est-à-dire distribuer CiviKey ou une application qui l'utilise sans dire clairement à vos utilisateurs, collègues, patrons ou investisseurs que vous l'utilisez.
  • Garder les modifications pour vous : c'est-à-dire faire évoluer CivKey lui-même (le noyau) sans remettre vos modifications dans le « pot commun » et rendre ces modifications visibles à toute personne qui le demande.
  • Interdire la copie ou la redistribution : ce n’est pas parce que votre application ou plugin propriétaire ne peut être copié ou redistribué que ces interdictions s’appliquent aux composants de CiviKey eux-mêmes.

En substance, vous pouvez utiliser les composants exécutables ou librairies dynamiques comme bon vous semble. Si vous modifiez les sources, vos modifications sont de facto sous licence L-GPL. A ce titre, nous apprécierons que vous les soumettiez à la communauté afin de faire progresser le projet.
Enfin, si vous « récupérez » un ou plusieurs fichiers, une classe ou un morceau de framework et que vous l’intégrer directement dans votre application (que celle-ci soit une librairie dynamique ou un exécutable), vous devez conserver l’entête du ou des fichiers, ne pas modifier leur contenu (sauf à publier ses modifications) et surtout, afficher le fait que votre application repose, en partie, sur un « morceau » de CiviKey.

Il n’y a donc finalement que quatre écueils à éviter qui ont pour nom : Plagiat, Dissimulation, Privatisation et Restriction.
Si vous échouez sur un de ces écueils, vous vous asseyez tranquillement sur notre travail. Ce ne serait pas gentil.

Le Blog...

Aujourd’hui, quelques réflexions disparates sur le blog lui-même.

1. De la raison d’être d’un Blog

Il n’a de sens (de mon point de vue) qu’en tant que moyen de communication (et non de promotion… n’est-ce pas Jean ?). On ne dit pas tout le temps la même chose à tout le monde. Je pars du principe qu’il y a donc nécessairement dans ces logorrhées des parties que l’on souhaite ne partager qu’avec un sous ensemble de son public habituel.
 
J’ai dans l’idée de répondre à ces enjeux de cibles et de contenu en jouant à deux niveaux différents qui me semblent complémentaires (et tous deux nécessaires). Ces deux mécanismes s’appliquent à des granularités différentes : le Post dans son ensemble et au sein du texte lui-même.

2. Catégories, Canaux de diffusion & Sécurité(s)

Gérer la diffusion du Post lui-même se fait très simplement grâce aux catégories que l’on peut associer à tout Post d’une part et d’autre part par la configuration du site qui héberge le blog.
Exemple de configuration :

  • La page Cuke qui porte le blog lui-même (et où l’on peut ajouter un nouveau Post) n’est accessible qu’à l’auteur et éventuellement d’autres personnes avec lesquelles l’auteur n’a pas de secrets).
  • L’affichage du blog sur cette page est sans restriction : aucun filtre de catégories n’est actif.
  • Des modules d’affichage de Blog sont disposés sur n’importe quelle page du site et filtre les Post par leurs catégories : les ACLs filtrent les utilisateurs et les Catégories filtrent le contenu.
  • Note sur la sécurité
    • C’est le Blog qui est sécurisé : en terme d’ACL on considère que les Posts partagent, héritent, de l’ACL du Blog auquel ils appartiennent.
    • Cela suppose, par exemple, que les liens « lire la suite » (s’ils existent) affichés par le module d’affichage de Blog sont cryptées : un Post n’est pas protégé en soi, il ne l’est que parce qu’un utilisateur lambda ne peut y accéder. Attention, ce n’est pas une réelle protection, il ne s’agit que d’une faible « protection par l’obscurité » en cachant le canal et non l’objet.

D’un point de vue strict, les Posts ne sont réellement sécurisés que via l’ACL du Blog. L’utilisation de la catégorisation est un moyen de gérer la « diffusion multi-canaux », pas de sécuriser le contenu (si vous ne saisissez pas l’importance de cette distinction, demandez moi de vous l’expliquer, c’est crucial J).
Ce choix est un compromis. Rien n’empêche de permettre la définition d’un ACL spécifique au niveau de chaque Post si nécessaire, l’ACL du Blog container étant simplement la protection par défaut.

3. Les appartés

Au sein du texte lui-même, l’idée est de pouvoir facilement faire des apartés à destination d’une ou plusieurs personnes. Le but de ces îlots de contenu est d’apporter en situation, au plus près du contenu général, un éclairage différent en fonction du lecteur, par exemple :

  • Demander l’avis d’un collègue ou d’une communauté sur un point particulier.
  • Aller plus loin dans une explication : un développeur peut dans un aparté faire référence à une implémentation non publique… ou à un bug récent…
  • Faire une « Private joke ».
  • Poser une note pour soi même.

Ce filtrage peut-il reposer sur les catégories ? Non, je ne le pense pas, et ce pour deux raisons :

  • Ces apartés ciblent des lecteurs particuliers, dans des buts divers (cf. ci-dessus) certes, mais toujours à des destinataires explicites.
  • Sémantiquement, le contenu de ces bribes de texte est fondamentalement lié à leur environnement : ils n’apportent pas (ou ne devraient pas apporter) de nouvelles thématiques : ils décorent un contenu, ils se positionnent au niveau pragmatique plutôt que sémantique.

Comment offrir cette possibilité d’apartés à l’auteur ? En encadrant ces apartés par des balises explicites de protection. Ces balises définissent les cibles pouvant lire le contenu : il suffit d’énumérer simplement les noms (login) des utilisateurs (ou les noms des groupes d’utilisateurs) accrédités.
Proposition : [Cuke.Filter Allow="Guillaume, 12531, P.Cuke.G.Supervisors"]aparté[/Cuke.Filter]

4. Conséquences, impacts et effets de bord

Cette capacité introduit une nécessaire étape de parsing préalable à tout affichage mais des optimisations sont possibles en utilisant les attributs des textes html.
Le comportement à l’indexation est simple: aucun aparté ne doit être indexé avec le contenu. Si indexation il doit y avoir, celle-ci doit intégrer totalement la sécurité (les apartés doivent être indexés en tant que documents distincts qui référencent leur texte d’origine).
 
La gestion de la diffusion par catégories est simple et ne pose pas de problèmes particuliers. En revanche, les apartés introduisent nettement plus d’effets de bord. Une option peut être intéressante mais certainement à développer… un jour.

Quand j’entends le mot culture, je sors mon revolver !

Est-ce vraiment Göring qui a dit ça ? Réponse ici : http://tatoufaux.com/spip.php?article398 (vu le nom du site, on devine la réponse). La culture en question en ce qui me concerne est celle de l’utilisateur et du contenu d’un site web. Cela n’a jamais été une problématique simple (et avec les moteurs de recherche, le droit à l’à peu près est oublié).En l’occurrence, il s’agit de sortir un algorithme correct qui prenne simultanément en compte :

  • L’éventuel cookie de l’utilisateur
    • S’il y en a un, il peut être « PubUser » ou non. Un « PubUser » doit être redirigé a minima (pour la cohérence de la navigation) là où pour un non « PubUser » (typiquement un administrateur ou un gestionnaire de contenu), on peut éviter de rediriger.
    • Sans cookie, la requête est considérée comme une requête de moteur.
  • La culture indiquée dans l’url, le cookie ou les informations du navigateur, croisée avec la culture par défaut du portail ou ses cultures actives sans oublier le statut de l’utilisateur qui peut, ou non, avoir le droit de voir toutes les cultures d’un site indépendamment de la restriction que peut apporter un portail.
  • Le nom de domaine qui peut être associé à un portail, avec pour certains utilisateurs (les « PubUser » et les requêtes sans cookie) une redirection systématique vers le nom de domaine du portail (ce qui entraîne la perte de l’éventuel cookie).
  • La présence ou non d’une publication semi-statique (qui dépend de la page exacte et de la culture), celle-ci pouvant n’être qu’une simple réécriture d’url (c’est la page dynamique qui est utilisée).
  • Le mode en cours pour le site (url en .cuke ou utilisation directe de Cuke.aspx).

L’idée générale étant de rediriger vers l’url exacte pour les moteurs (ou toute requête sans cookie), cette url ne devant contenir la culture que pour les cultures qui ne sont pas la culture par défaut.
Ce n’est pas terminé, il manque des tests unitaires et certains mécanismes, notamment en ce qui concerne le filtre sur les cultures actives, la gestion des 404 et la prise de compte des pages virtuelles (K-Item)…
Pas facile la culture...

NextResult() and so what ?

J’ai parfois du mal à me souvenir du fonctionnement précis de certaines API. Le comportement du IDataReader avec plusieurs résultats en fait partie. Un petit test unitaire s’impose :

[Test]
public void TestDataReaderBehavior()
{
       ExecuteReader( "select 1;",
              delegate( IDataReader r )
       {
             Assert.IsTrue( r.Read(), "We are directly BEFORE the first record." );
             Assert.AreEqual( 1, r.GetInt32( 0 ) );
             Assert.IsFalse( r.Read() );
             Assert.IsFalse( r.Read(), "One can always call Read." );
             Assert.IsFalse( r.NextResult() );
             Assert.IsFalse( r.NextResult(), "One can always call NextResult." );
             Assert.IsFalse( r.IsClosed, "A data reader must explicitely be closed (except if obtained with ExecuteReader( CommandBehavior.CloseConnection )." );
       } );
 
       ExecuteReader( "select 1; select 2;",
              delegate( IDataReader r )
       {
             Assert.IsTrue( r.Read() );
             Assert.AreEqual( 1, r.GetInt32( 0 ) );
             Assert.IsTrue( r.NextResult() );
             Assert.IsTrue( r.Read(), "Here also we are BEFORE the first record." );
             Assert.AreEqual( 2, r.GetInt32( 0 ) );
             Assert.IsFalse( r.Read() );
             Assert.IsFalse( r.NextResult() );
       } );
 
       ExecuteReader( "select 1 from sys.tables where 0=1; select 2;", 
              delegate( IDataReader r )
       {
             Assert.IsFalse( r.Read(), "First select has no results." );
             Assert.IsTrue( r.NextResult() );
             Assert.IsTrue( r.Read() );
             Assert.AreEqual( 2, r.GetInt32( 0 ) );
             Assert.IsFalse( r.Read() );
             Assert.IsFalse( r.NextResult() );
       } );
 
       ExecuteReader( "select 1;select 'Impossible' from sys.tables where 0=1;select 3",
              delegate( IDataReader r )
       {
             Assert.IsTrue( r.Read() );
             Assert.AreEqual( 1, r.GetInt32( 0 ) );
             Assert.IsTrue( r.NextResult(), "There is a 2nd result..." );
             Assert.IsFalse( r.Read(), "...but it is empty." );
             Assert.IsTrue( r.NextResult(), "There is a 3rd result..." );
             Assert.IsTrue( r.Read() );
             Assert.AreEqual( 3, r.GetInt32( 0 ) );
             Assert.IsFalse( r.Read() );
             Assert.IsFalse( r.NextResult() );
             Assert.IsFalse( r.IsClosed );
       } );
}

Cela vaut bien un long discours, non ?
En tous cas, cela vaut mieux que la documentation MSDN ci-dessous qui annonce faussement qu’après l’appel à NextResult, le lecteur est positionné sur le premier enregistrement. J’ai signalé l’erreur (après m’être créé un login .Net passport suite à des années de résistance).

Je vais pouvoir compter les jours avant la correction Smile.
 
Update jeudi 8 octobre 2009 : Il n’y a toujours pas eu de corrections mais une contribution supplémentaire de David Matson qui considère que l’interprétation de la remarque doit se comprendre au niveau de chaque result set et non au niveau des lignes.

Il a complètement raison ce monsieur !