Obtenir une couleur plus sombre ou plus claire pour CSS [javascript]

EDIT du 22/05/2012 : il existe un site qui permet de trouver plusieurs dégradés à partir d'une couleur, et ça se passe sur http://0to255.com/
EDIT du 20/09/2012 : et voilà encore un autre site un peu plus intéressant que le précédent http://sassme.arc90.com/
EDIT du 25/09/2012 : une autre fonction/méthode plus légère est disponible tout en bas du billet

Voici un petit outil en ligne qui permet d'avoir une couleur plus claire ou plus sombre en se basant sur une couleur CSS et en demandant un pourcentage (exprimé entre 0 et 1) à appliquer :



Résultat:

Cette fonction vient directement de stackoverflow.com :

var pad = function(num, totalChars) {
    var pad = '0';
    num = num + '';
    while (num.length < totalChars) {
        num = pad + num;
    }
    return num;
};

// Ratio is between 0 and 1
var changeColor = function(color, ratio, darker) {
    // Trim trailing/leading whitespace
    color = color.replace(/^\s*|\s*$/, '');

    // Expand three-digit hex
    color = color.replace(
        /^#?([a-f0-9])([a-f0-9])([a-f0-9])$/i,
        '#$1$1$2$2$3$3'
    );

    // Calculate ratio
    var difference = Math.round(ratio * 256) * (darker ? -1 : 1),
        // Determine if input is RGB(A)
        rgb = color.match(new RegExp('^rgba?\\(\\s*' +
            '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
            '\\s*,\\s*' +
            '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
            '\\s*,\\s*' +
            '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
            '(?:\\s*,\\s*' +
            '(0|1|0?\\.\\d+))?' +
            '\\s*\\)$'
        , 'i')),
        alpha = !!rgb && rgb[4] != null ? rgb[4] : null,

        // Convert hex to decimal
        decimal = !!rgb? [rgb[1], rgb[2], rgb[3]] : color.replace(
            /^#?([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])/i,
            function() {
                return parseInt(arguments[1], 16) + ',' +
                    parseInt(arguments[2], 16) + ',' +
                    parseInt(arguments[3], 16);
            }
        ).split(/,/),
        returnValue;

    // Return RGB(A)
    return !!rgb ?
        'rgb' + (alpha !== null ? 'a' : '') + '(' +
            Math[darker ? 'max' : 'min'](
                parseInt(decimal[0], 10) + difference, darker ? 0 : 255
            ) + ', ' +
            Math[darker ? 'max' : 'min'](
                parseInt(decimal[1], 10) + difference, darker ? 0 : 255
            ) + ', ' +
            Math[darker ? 'max' : 'min'](
                parseInt(decimal[2], 10) + difference, darker ? 0 : 255
            ) +
            (alpha !== null ? ', ' + alpha : '') +
            ')' :
        // Return hex
        [
            '#',
            pad(Math[darker ? 'max' : 'min'](
                parseInt(decimal[0], 10) + difference, darker ? 0 : 255
            ).toString(16), 2),
            pad(Math[darker ? 'max' : 'min'](
                parseInt(decimal[1], 10) + difference, darker ? 0 : 255
            ).toString(16), 2),
            pad(Math[darker ? 'max' : 'min'](
                parseInt(decimal[2], 10) + difference, darker ? 0 : 255
            ).toString(16), 2)
        ].join('');
};
var lighterColor = function(color, ratio) {
    return changeColor(color, ratio, false);
};
var darkerColor = function(color, ratio) {
    return changeColor(color, ratio, true);
};

// Use
var darker = darkerColor('rgba(80, 75, 52, .5)', .2);
var lighter = lighterColor('rgba(80, 75, 52, .5)', .2);

Une autre méthode, plus légère, inspirée par stackoverflow:

function LightenDarkenColor(col,amt,usePound,num,f,h,r,b,g) {
  if (col[0]=="#") {
    col = col.slice(1);
    usePound = (usePound==undefined?true:usePound)
  }

  num = parseInt(col,16);
  f=function(n) { return n>255?255:(n<0?0:n) }
  h=function(n) { return n.length<2?"0"+n:n }
  
  r = h(f((num >> 16) + amt).toString(16));
  b = h(f(((num >> 8) & 0x00FF) + amt).toString(16));
  g = h(f((num & 0x0000FF) + amt).toString(16));

  return (usePound?"#":"") + r + b + g;
}

LightenDarkenColor("#FAB787",10);
LightenDarkenColor("#FAB787",-15);
LightenDarkenColor("FAB787",5,true);

A console program for Newsgroup post [linux]

I spent a long time to find how to post on newsgroup with Linux in console mode only.
Here is the solution !

Download and compile newspost

Newspost is a program to post on the newsgroup. I’m on Debian but this package doesn’t exist anymore. However you can download the source and compile them… except there are few errors with the original sources (at least on my box). The issue is the source uses a function called « getfile », however this function name is already used in something else and it causes a conflict.
So I’ve renamed the function and it now works.

Download newspost-2.1.1.orig.tar.gz

Extract the files, then use make && make install

Install par2 and rar

On a Debian you’ll do apt-get install par2 rar

Let’s go

The first step is to split your file into small ones with rar extension :
rar a "name of rar file" -v20m -m0 "/home/example/your file"

Here are some explanations regarding this command :

  • name of rar file : This is the name of the rar-files you’re going to create (« .rar » will automatic be added)
  • -v20m : Split the file into 20MB chunks
  • -m0 : Set compression level (0-store…3-default…5-best)
  • « /home/example/your file » : The file you want to rar

Now we can create the .par files :
par2create -r10 -n7 "name of .par files" "/home/example/the .rar files*"

Some explanations :

  • -r15 : Percent par-files you want to create
  • -n7 : Number of par-files you want to create
  • /home/example/the .rar files* » : The just created .rar files

Now it’s time to upload your files :
newspost -i SERVER.NEWSGROUP.COM -u USERNAME -p PASSWORD -f EMAIL@EXAMPLE.COM -n ALT.BINARIES.YOUR.GROUP -y -s "SUBJECT" "/home/example/*par*"

So replace the uppercase words with your own values.

Thanks to this old post !

SharepointPlus 2.5.1 released [announcement]

I’ve just released SharepointPlus 2.5.1 !

This new version brings two functions :
$SP.people() that will permit you to find the user details (email, manager, departement, job title, …) from his name;
$SP.addressbook() that is a search feature to find anyone from the Active Directory using a part of a name.

Enhance your Sharepoint with SharepointPlus 🙂

Datepicker pour Bootstrap [Javascript]

Edit 5/04/2012 : d’autres personnes ont fait comme moi, et vous pouvez trouver leur repository sur https://github.com/eternicode/bootstrap-datepicker — ils ont corrigé les mêmes bugs visiblement et ont ajouté quelques petites choses comme le support i18n (internationalisation du format de la date)

J’ai découvert un datepicker qui utilise l’excellent Bootstrap de Twitter (vous savez c’est un ensemble de CSS qu’on peut utiliser pour des effets très réussis), et qui rend comme ça :

Je me suis empressé de l’utiliser dans un projet, jusqu’à ce que je me rende compte qu’il ne fonctionne pas avec Bootstrap v2.0.2, et ni avec IE7+ …. Du coup j’ai modifié le code d’origine pour outrepasser ces divers problèmes (plus d’autres petites choses). J’aurais proposé mon patch au créateur s’il utilisait GitHub, mais ce n’est pas le cas, donc j’ai créé un repository pour ce projet là : https://github.com/Aymkdn/Datepicker-for-Bootstrap

Il ressemble en tout point à la version d’origine, sauf qu’il fonctionne maintenant pour IE et pour la dernière version de Bootstrap. Il permet aussi d’avoir la date du jour comme date par défaut (au lieu du 1/1/1970 ….), et évite d’avoir une date sélectionnée automatiquement lorsqu’on ne fait que passer par le champ en utilisant la touche TAB.

Et bien sûr il y a la démo.

Public release of SharepointPlus — a JavaScript API for Sharepoint [announcement]

In my job I have to work with Sharepoint, but with no access to the back-end. I can only edit the HTML code, and hopefully I can use JavaScript. It’s really handy with the Sharepoint web services, even if they are not well-documented !

I found a couple of JS API on the Web, but there are not documented or not really easy to use. Then I developed my own API: SharepointPlus.

And today I announce the first public release of my framework available on GitHub: http://aymkdn.github.com/SharepointPlus/

Share the love 🙂

Changer le thème/skin de Pidgin [Astuce]

Pidgin est un logiciel qui permet de se connecter à MSN, GTalk, Facebook Chat, et bien d’autres protocoles.

J’ai eu quelques difficultés à trouver comment changer le thème de ce programme… Et à force de chercher je suis tombé sur la documentation (qui est très mal référencée) que je vous traduis ci-dessous :

Comment changer mon thème GTK+ ?

Depuis la version 2.7.0, Pidgin s’accompagne d’une version intégrée de GTK+ qui n’inclue pas de thèmes supplémentaires ou d’application pour en changer.

Pourtant il existe un moyen de le faire grâce à Alexander Shaduri. Remarque : il n’est pas recommandé d’utiliser l’installeur de GTK+ qu’il distribue, à moins que vous ne saviez ce que vous faites et que vous le fassiez pour de bonnes raisons.

Commencez par télécharger GTK2 Thèmes que vous allez extraire dans un dossier temporaire pour y récupérer les répertoires lib et share que vous allez ensuite placer dans <répertoire d'installation de Pidgin>\Gtk (ils existent déjà donc il faut les ajouter quand même).
Ensuite télécharger GTK2 Prefs que vous allez décompresser dans un dossier temporaire pour récupérer le fichier gtk2_prefs.exe qu’il faudra placer dans le répertoire <répertoire d'installation de Pidgin>\Gtk\bin.

Il ne vous reste plus qu’à exécuter gtk2_prefs.exe et de choisir le thème. Vous voudrez probablement choisir l’option « Apply for all users », et vous devrez relancer Pidgin pour qu’il applique le nouveau thème.

URL dans un Workflow de Sharepoint [Astuce]

Avec les workflows de Sharepoint on peut faire un certain nombre de choses, comme envoyer des emails avec des liens.
Il existe trois variable dans la Current List à savoir :
Encoded Absolute Url : retourne une url du type http://votre.site.com/votre/rep/Lists/NomListe/2_.000 avec ‘2’ l’ID de l’item… donc pas très utile;
Server Relative Url : retourne /votre/rep/Lists/NomListe/2_.000 …. donc également pas très utile;
Path : retourne votre/rep/Lists/NomListe qui devient plus exploitable !

Ainsi vous allez pouvoir créer des liens dans vos emails de workflow qui redirige vers la liste courante grâce à Path et en y ajoutant ce qui va bien ! 🙂

Attention cependant, puisque Sharepoint gère incorrectement les espaces dans l’adresse URL, et le premier espace qu’il y aura sera tout simplement supprimé….

Permettre le retour en arrière sur une page PHP avec une session [programmation]

Si vous utilisez les sessions avec PHP et que vous avez un formulaire qui fait du POST, alors le retour en arrière (en utilisant le bouton du navigateur) risque de retourner une page blanche avec un message d’erreur du style « Le document a expiré ».

Après avoir fouillé la documentation PHP pour session_cache_limiter, j’ai découvert qu’il suffit d’ajouter session_cache_limiter('private_no_expire, must-revalidate'); au tout début du fichier, avant quoi que ce soit. Et maintenant le retour en arrière fonctionne !

Modifier les permissions d’une liste Sharepoint en utilisant permissions.aspx et JavaScript [Sharepoint]

Il est possible d’utiliser les web services de Sharepoint pour effectuer un certain nombre d’opérations. Il existe divers API (comme http://spservices.codeplex.com que j’utiliserai ici) pour aider dans ces opérations.

Cependant il est difficile de trouver de la documentation / exemples sur comment utiliser AddPermission, RemovePermission et UpdatePermissions. Après avoir longuement fouillé le Net, voici ce que j’ai trouvé.

Tout d’abord, pour assigner un rôle défini sur votre site (comme « Contribute », « Full Control », « Read », etc) il va falloir trouver les droits associés. Pour cela on utilise GetRoleCollectionFromWeb

$().SPServices({
  operation:"GetRoleCollectionFromWeb",
  webURL:"http://mon.site.com/ma/collection/",
  completefunc: function (xData, Status) {
    var roles=[]; // va contenir les différents rôles existants et les permissions associées
    var dom=xData.responseXML.getElementsByTagName("Role");
    for (var i=dom.length; i--;) roles[dom[i].getAttribute("Name")]=dom[i].getAttribute("BasePermissions").split(", ");
  }
});

Maintenant on va avoir un tableau associatif qui lie un rôle (par exemple « Contribute ») à un ensemble de permissions (ici ça sera « ViewListItems, AddListItems, EditListItems, DeleteListItems, OpenItems, ViewVersions, DeleteVersions, ManagePersonalViews, ViewFormPages, Open, ViewPages, CreateSSCSite, BrowseDirectories, BrowseUserInfo, AddDelPrivateWebParts, UpdatePersonalWebParts, UseClientIntegration, UseRemoteAPIs, CreateAlerts, EditMyUserInfo »).

Grâce à la documentation de Microsoft portant sur les masks de permissions on va créer un autre tableau associatif pour chacune de ces BasePermissions.
Aussi on sait qu’il faut faire un OR entre chaque mask pour avoir celui qu’on souhaite.

// @param basePermissions est un tableau
function getPermissionMask(basePermissions) {
  // on crée un tableau basé sur la doc Microsoft
  var permissions=[];
  // on rajoute "FullMask" qui est le Full Control
  permissions["FullMask"] = -1;
  permissions["ViewListItems"] = parseInt("0x0000000000000001");
  permissions["AddListItems"] = parseInt("0x0000000000000002");
  permissions["EditListItems"] = parseInt("0x0000000000000004");
  permissions["DeleteListItems"] = parseInt("0x0000000000000008");
  permissions["ApproveItems"] = parseInt("0x0000000000000010");
  permissions["OpenItems"] = parseInt("0x0000000000000020");
  permissions["ViewVersions"] = parseInt("0x0000000000000040");
  permissions["DeleteVersions"] = parseInt("0x0000000000000080");
  permissions["CancelCheckout"] = parseInt("0x0000000000000100");
  permissions["ManagePersonalViews"] = parseInt("0x0000000000000200");
  permissions["ManageLists"] = parseInt("0x0000000000000800");
  permissions["ViewFormPages"] = parseInt("0x0000000000001000");
  permissions["Open"] = parseInt("0x0000000000010000");
  permissions["ViewPages"] = parseInt("0x0000000000020000");
  permissions["AddAndCustomizePages"] = parseInt("0x0000000000040000");
  permissions["ApplyThemeAndBorder"] = parseInt("0x0000000000080000");
  permissions["ApplyStyleSheets"] = parseInt("0x0000000000100000");
  permissions["ViewUsageData"] = parseInt("0x0000000000200000");
  permissions["CreateSSCSite"] = parseInt("0x0000000000400000");
  permissions["ManageSubwebs"] = parseInt("0x0000000000800000");
  permissions["CreateGroups"] = parseInt("0x0000000001000000");
  permissions["ManagePermissions"] = parseInt("0x0000000002000000");
  permissions["BrowseDirectories"] = parseInt("0x0000000004000000");
  permissions["BrowseUserInfo"] = parseInt("0x0000000008000000");
  permissions["AddDelPrivateWebParts"] = parseInt("0x0000000010000000");
  permissions["UpdatePersonalWebParts"] = parseInt("0x0000000020000000");
  permissions["ManageWeb"] = parseInt("0x0000000040000000");
  permissions["UseClientIntegration"] = parseInt("0x0000001000000000");
  permissions["UseRemoteAPIs"] = parseInt("0x0000002000000000");
  permissions["ManageAlerts"] = parseInt("0x0000004000000000");
  permissions["CreateAlerts"] = parseInt("0x0000008000000000");
  permissions["EditMyUserInfo"] = parseInt("0x0000010000000000");

  //maintenant on va faire des OR selon basePermissions
  var mask = 1;
  for (var i=basePermissions.length; i--;) mask |= permissions[basePermissions[i]];
  return mask;
}

Et enfin on utilise ce mask avec AddPermission :

$().SPServices({
  operation:"AddPermission",
  webURL:"http://mon.site.com/ma/collection/",
  objectName:"Nom de la Liste",
  objectType:"List",
  permissionIdentifier:"domain\\login", // par exemple "europe\\john_doe"
  permissionType:"user", // car c'est un username qu'on a donné... si on avait donné un Sharepoint Group alors on aurait mis "group"
  permissionMask:getPermissionMask(roles["Contribute"]),
  completefunc: function (xData, Status) { alert("Permissions ajoutés"); }
});

Plus d’infos sur les arguments disponibles dans la documentation de SPServices sur les Permissions.

On peut enfin tout mettre ensemble, ce qui donnera :

// @param basePermissions est un tableau
function getPermissionMask(basePermissions) {
  // on crée un tableau basé sur la doc Microsoft
  var permissions=[];
  // on rajoute "FullMask" qui est le Full Control
  permissions["FullMask"] = -1;
  permissions["ViewListItems"] = parseInt("0x0000000000000001");
  permissions["AddListItems"] = parseInt("0x0000000000000002");
  permissions["EditListItems"] = parseInt("0x0000000000000004");
  permissions["DeleteListItems"] = parseInt("0x0000000000000008");
  permissions["ApproveItems"] = parseInt("0x0000000000000010");
  permissions["OpenItems"] = parseInt("0x0000000000000020");
  permissions["ViewVersions"] = parseInt("0x0000000000000040");
  permissions["DeleteVersions"] = parseInt("0x0000000000000080");
  permissions["CancelCheckout"] = parseInt("0x0000000000000100");
  permissions["ManagePersonalViews"] = parseInt("0x0000000000000200");
  permissions["ManageLists"] = parseInt("0x0000000000000800");
  permissions["ViewFormPages"] = parseInt("0x0000000000001000");
  permissions["Open"] = parseInt("0x0000000000010000");
  permissions["ViewPages"] = parseInt("0x0000000000020000");
  permissions["AddAndCustomizePages"] = parseInt("0x0000000000040000");
  permissions["ApplyThemeAndBorder"] = parseInt("0x0000000000080000");
  permissions["ApplyStyleSheets"] = parseInt("0x0000000000100000");
  permissions["ViewUsageData"] = parseInt("0x0000000000200000");
  permissions["CreateSSCSite"] = parseInt("0x0000000000400000");
  permissions["ManageSubwebs"] = parseInt("0x0000000000800000");
  permissions["CreateGroups"] = parseInt("0x0000000001000000");
  permissions["ManagePermissions"] = parseInt("0x0000000002000000");
  permissions["BrowseDirectories"] = parseInt("0x0000000004000000");
  permissions["BrowseUserInfo"] = parseInt("0x0000000008000000");
  permissions["AddDelPrivateWebParts"] = parseInt("0x0000000010000000");
  permissions["UpdatePersonalWebParts"] = parseInt("0x0000000020000000");
  permissions["ManageWeb"] = parseInt("0x0000000040000000");
  permissions["UseClientIntegration"] = parseInt("0x0000001000000000");
  permissions["UseRemoteAPIs"] = parseInt("0x0000002000000000");
  permissions["ManageAlerts"] = parseInt("0x0000004000000000");
  permissions["CreateAlerts"] = parseInt("0x0000008000000000");
  permissions["EditMyUserInfo"] = parseInt("0x0000010000000000");

  //maintenant on va faire des OR selon basePermissions
  var mask = 1;
  for (var i=basePermissions.length; i--;) mask |= permissions[basePermissions[i]];
  return mask;
}

$().SPServices({
  operation:"GetRoleCollectionFromWeb",
  webURL:"http://mon.site.com/ma/collection/",
  completefunc: function (xData, Status) {
    var roles=[]; // va contenir les différents rôles existants et les permissions associées
    var dom=xData.responseXML.getElementsByTagName("Role");
    for (var i=dom.length; i--;) roles[dom[i].getAttribute("Name")]=dom[i].getAttribute("BasePermissions").split(", ");
    $().SPServices({
      operation:"AddPermission",
      webURL:"http://mon.site.com/ma/collection/",
      objectName:"Nom de la Liste",
      objectType:"List",
      permissionIdentifier:"domain\\login", // par exemple "europe\\john_doe"
      permissionType:"user", // car c'est un username qu'on a donné... si on avait donné un Sharepoint Group alors on aurait mis "group"
      permissionMask:getPermissionMask(roles["Contribute"]),
      completefunc: function (xData, Status) { alert("Permissions ajoutés"); }
    });
  }
});

A noter :
UpdatePermission va remplacer les permissions d’une personne;
– Toute personnalisation des permissions sur la liste va entrainer l’arrêt des droits inhérents (avec Sharepoint 2010 il sera possible de remettre les droits parents grâce à How to: Break Role Assignment Inheritance Using ECMAScript)