Browse Source

Mise en place de 2 plugins autour des rôles sur les liaisons :

- «roles» gère l'API de rôles, et la surcharge des fichiers de SPIP (formulaire d'édition de lien et API de lien) et la mise en place d'outils pour l'interface utilisateur (chosen, et un peu de bootstrap). Il manque 2 fichiers qui arriveront après.

- «roles_auteurs» est un exemple de mise en place de l'API de rôles pour la liaison entre auteurs et articles.
v1
marcimat@rezo.net 10 years ago
commit
7e76bd5b98
  1. 24
      .gitattributes
  2. 666
      css/bootstrap-button-dropdown.css
  3. 32
      formulaires/inc-editer_liens_actions_roles.html
  4. 9
      formulaires/inc-editer_liens_roles.html
  5. 319
      inc/roles.php
  6. 100
      javascript/bootstrap-dropdown.js
  7. 9
      javascript/roles.js
  8. 14
      lang/paquet-roles_fr.php
  9. 15
      lang/roles_fr.php
  10. BIN
      lib/chosen/chosen-sprite.png
  11. 396
      lib/chosen/chosen.css
  12. 1003
      lib/chosen/chosen.jquery.js
  13. 10
      lib/chosen/chosen.jquery.min.js
  14. 1007
      lib/chosen/chosen.proto.js
  15. 10
      lib/chosen/chosen.proto.min.js
  16. 10
      notes.txt
  17. 22
      paquet.xml
  18. 19
      prive/style_prive_plugin_roles.html
  19. BIN
      prive/themes/spip/images/inserer-8.png
  20. BIN
      prive/themes/spip/images/roles-128.png
  21. BIN
      prive/themes/spip/images/roles-32.png
  22. BIN
      prive/themes/spip/images/roles-64.png
  23. 31
      roles_fonctions.php
  24. 45
      roles_pipelines.php

24
.gitattributes vendored

@ -0,0 +1,24 @@
* text=auto !eol
css/bootstrap-button-dropdown.css -text
formulaires/inc-editer_liens_actions_roles.html -text
formulaires/inc-editer_liens_roles.html -text
inc/roles.php -text
javascript/bootstrap-dropdown.js -text
javascript/roles.js -text
lang/paquet-roles_fr.php -text
lang/roles_fr.php -text
lib/chosen/chosen-sprite.png -text
lib/chosen/chosen.css -text
lib/chosen/chosen.jquery.js -text
lib/chosen/chosen.jquery.min.js -text
lib/chosen/chosen.proto.js -text
lib/chosen/chosen.proto.min.js -text
/notes.txt -text
/paquet.xml -text
prive/style_prive_plugin_roles.html -text
prive/themes/spip/images/inserer-8.png -text
prive/themes/spip/images/roles-128.png -text
prive/themes/spip/images/roles-32.png -text
prive/themes/spip/images/roles-64.png -text
/roles_fonctions.php -text
/roles_pipelines.php -text

666
css/bootstrap-button-dropdown.css vendored

@ -0,0 +1,666 @@
/*!
* Bootstrap v2.0.4
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/
.clearfix {
*zoom: 1;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.hide-text {
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.input-block-level {
display: block;
width: 100%;
min-height: 28px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.btn {
display: inline-block;
*display: inline;
/* IE7 inline-block hack */
*zoom: 1;
padding: 4px 10px 4px;
margin-bottom: 0;
font-size: 13px;
line-height: 18px;
*line-height: 20px;
color: #333333;
text-align: center;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
vertical-align: middle;
cursor: pointer;
background-color: #f5f5f5;
background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
background-image: linear-gradient(top, #ffffff, #e6e6e6);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #e6e6e6;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border: 1px solid #cccccc;
*border: 0;
border-bottom-color: #b3b3b3;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
*margin-left: .3em;
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
}
.btn:hover,
.btn:active,
.btn.active,
.btn.disabled,
.btn[disabled] {
background-color: #e6e6e6;
*background-color: #d9d9d9;
}
.btn:active,
.btn.active {
background-color: #cccccc \9;
}
.btn:first-child {
*margin-left: 0;
}
.btn:hover {
color: #333333;
text-decoration: none;
background-color: #e6e6e6;
*background-color: #d9d9d9;
/* Buttons in IE7 don't get borders, so darken on hover */
background-position: 0 -15px;
-webkit-transition: background-position 0.1s linear;
-moz-transition: background-position 0.1s linear;
-ms-transition: background-position 0.1s linear;
-o-transition: background-position 0.1s linear;
transition: background-position 0.1s linear;
}
.btn:focus {
outline: thin dotted #333;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
.btn.active,
.btn:active {
background-color: #e6e6e6;
background-color: #d9d9d9 \9;
background-image: none;
outline: 0;
-webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
}
.btn.disabled,
.btn[disabled] {
cursor: default;
background-color: #e6e6e6;
background-image: none;
opacity: 0.65;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
.btn-large {
padding: 9px 14px;
font-size: 15px;
line-height: normal;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.btn-large [class^="icon-"] {
margin-top: 1px;
}
.btn-small {
padding: 5px 9px;
font-size: 11px;
line-height: 16px;
}
.btn-small [class^="icon-"] {
margin-top: -1px;
}
.btn-mini {
padding: 2px 6px;
font-size: 11px;
line-height: 14px;
}
.btn-primary,
.btn-primary:hover,
.btn-warning,
.btn-warning:hover,
.btn-danger,
.btn-danger:hover,
.btn-success,
.btn-success:hover,
.btn-info,
.btn-info:hover,
.btn-inverse,
.btn-inverse:hover {
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.btn-primary.active,
.btn-warning.active,
.btn-danger.active,
.btn-success.active,
.btn-info.active,
.btn-inverse.active {
color: rgba(255, 255, 255, 0.75);
}
.btn {
border-color: #ccc;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
}
.btn-primary {
background-color: #0074cc;
background-image: -moz-linear-gradient(top, #0088cc, #0055cc);
background-image: -ms-linear-gradient(top, #0088cc, #0055cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0055cc);
background-image: -o-linear-gradient(top, #0088cc, #0055cc);
background-image: linear-gradient(top, #0088cc, #0055cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);
border-color: #0055cc #0055cc #003580;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #0055cc;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.btn-primary:hover,
.btn-primary:active,
.btn-primary.active,
.btn-primary.disabled,
.btn-primary[disabled] {
background-color: #0055cc;
*background-color: #004ab3;
}
.btn-primary:active,
.btn-primary.active {
background-color: #004099 \9;
}
.btn-warning {
background-color: #faa732;
background-image: -moz-linear-gradient(top, #fbb450, #f89406);
background-image: -ms-linear-gradient(top, #fbb450, #f89406);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
background-image: -o-linear-gradient(top, #fbb450, #f89406);
background-image: linear-gradient(top, #fbb450, #f89406);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);
border-color: #f89406 #f89406 #ad6704;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #f89406;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.btn-warning:hover,
.btn-warning:active,
.btn-warning.active,
.btn-warning.disabled,
.btn-warning[disabled] {
background-color: #f89406;
*background-color: #df8505;
}
.btn-warning:active,
.btn-warning.active {
background-color: #c67605 \9;
}
.btn-danger {
background-color: #da4f49;
background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
background-image: -ms-linear-gradient(top, #ee5f5b, #bd362f);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
background-image: linear-gradient(top, #ee5f5b, #bd362f);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);
border-color: #bd362f #bd362f #802420;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #bd362f;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.btn-danger:hover,
.btn-danger:active,
.btn-danger.active,
.btn-danger.disabled,
.btn-danger[disabled] {
background-color: #bd362f;
*background-color: #a9302a;
}
.btn-danger:active,
.btn-danger.active {
background-color: #942a25 \9;
}
.btn-success {
background-color: #5bb75b;
background-image: -moz-linear-gradient(top, #62c462, #51a351);
background-image: -ms-linear-gradient(top, #62c462, #51a351);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
background-image: -webkit-linear-gradient(top, #62c462, #51a351);
background-image: -o-linear-gradient(top, #62c462, #51a351);
background-image: linear-gradient(top, #62c462, #51a351);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);
border-color: #51a351 #51a351 #387038;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #51a351;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.btn-success:hover,
.btn-success:active,
.btn-success.active,
.btn-success.disabled,
.btn-success[disabled] {
background-color: #51a351;
*background-color: #499249;
}
.btn-success:active,
.btn-success.active {
background-color: #408140 \9;
}
.btn-info {
background-color: #49afcd;
background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
background-image: -ms-linear-gradient(top, #5bc0de, #2f96b4);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
background-image: linear-gradient(top, #5bc0de, #2f96b4);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);
border-color: #2f96b4 #2f96b4 #1f6377;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #2f96b4;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.btn-info:hover,
.btn-info:active,
.btn-info.active,
.btn-info.disabled,
.btn-info[disabled] {
background-color: #2f96b4;
*background-color: #2a85a0;
}
.btn-info:active,
.btn-info.active {
background-color: #24748c \9;
}
.btn-inverse {
background-color: #414141;
background-image: -moz-linear-gradient(top, #555555, #222222);
background-image: -ms-linear-gradient(top, #555555, #222222);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));
background-image: -webkit-linear-gradient(top, #555555, #222222);
background-image: -o-linear-gradient(top, #555555, #222222);
background-image: linear-gradient(top, #555555, #222222);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);
border-color: #222222 #222222 #000000;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
*background-color: #222222;
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
}
.btn-inverse:hover,
.btn-inverse:active,
.btn-inverse.active,
.btn-inverse.disabled,
.btn-inverse[disabled] {
background-color: #222222;
*background-color: #151515;
}
.btn-inverse:active,
.btn-inverse.active {
background-color: #080808 \9;
}
button.btn,
input[type="submit"].btn {
*padding-top: 2px;
*padding-bottom: 2px;
}
button.btn::-moz-focus-inner,
input[type="submit"].btn::-moz-focus-inner {
padding: 0;
border: 0;
}
button.btn.btn-large,
input[type="submit"].btn.btn-large {
*padding-top: 7px;
*padding-bottom: 7px;
}
button.btn.btn-small,
input[type="submit"].btn.btn-small {
*padding-top: 3px;
*padding-bottom: 3px;
}
button.btn.btn-mini,
input[type="submit"].btn.btn-mini {
*padding-top: 1px;
*padding-bottom: 1px;
}
.btn-group {
position: relative;
*zoom: 1;
*margin-left: .3em;
}
.btn-group:before,
.btn-group:after {
display: table;
content: "";
}
.btn-group:after {
clear: both;
}
.btn-group:first-child {
*margin-left: 0;
}
.btn-group + .btn-group {
margin-left: 5px;
}
.btn-toolbar {
margin-top: 9px;
margin-bottom: 9px;
}
.btn-toolbar .btn-group {
display: inline-block;
*display: inline;
/* IE7 inline-block hack */
*zoom: 1;
}
.btn-group > .btn {
position: relative;
float: left;
margin-left: -1px;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.btn-group > .btn:first-child {
margin-left: 0;
-webkit-border-top-left-radius: 4px;
-moz-border-radius-topleft: 4px;
border-top-left-radius: 4px;
-webkit-border-bottom-left-radius: 4px;
-moz-border-radius-bottomleft: 4px;
border-bottom-left-radius: 4px;
}
.btn-group > .btn:last-child,
.btn-group > .dropdown-toggle {
-webkit-border-top-right-radius: 4px;
-moz-border-radius-topright: 4px;
border-top-right-radius: 4px;
-webkit-border-bottom-right-radius: 4px;
-moz-border-radius-bottomright: 4px;
border-bottom-right-radius: 4px;
}
.btn-group > .btn.large:first-child {
margin-left: 0;
-webkit-border-top-left-radius: 6px;
-moz-border-radius-topleft: 6px;
border-top-left-radius: 6px;
-webkit-border-bottom-left-radius: 6px;
-moz-border-radius-bottomleft: 6px;
border-bottom-left-radius: 6px;
}
.btn-group > .btn.large:last-child,
.btn-group > .large.dropdown-toggle {
-webkit-border-top-right-radius: 6px;
-moz-border-radius-topright: 6px;
border-top-right-radius: 6px;
-webkit-border-bottom-right-radius: 6px;
-moz-border-radius-bottomright: 6px;
border-bottom-right-radius: 6px;
}
.btn-group > .btn:hover,
.btn-group > .btn:focus,
.btn-group > .btn:active,
.btn-group > .btn.active {
z-index: 2;
}
.btn-group .dropdown-toggle:active,
.btn-group.open .dropdown-toggle {
outline: 0;
}
.btn-group > .dropdown-toggle {
padding-left: 8px;
padding-right: 8px;
-webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
*padding-top: 4px;
*padding-bottom: 4px;
}
.btn-group > .btn-mini.dropdown-toggle {
padding-left: 5px;
padding-right: 5px;
}
.btn-group > .btn-small.dropdown-toggle {
*padding-top: 4px;
*padding-bottom: 4px;
}
.btn-group > .btn-large.dropdown-toggle {
padding-left: 12px;
padding-right: 12px;
}
.btn-group.open .dropdown-toggle {
background-image: none;
-webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
}
.btn-group.open .btn.dropdown-toggle {
background-color: #e6e6e6;
}
.btn-group.open .btn-primary.dropdown-toggle {
background-color: #0055cc;
}
.btn-group.open .btn-warning.dropdown-toggle {
background-color: #f89406;
}
.btn-group.open .btn-danger.dropdown-toggle {
background-color: #bd362f;
}
.btn-group.open .btn-success.dropdown-toggle {
background-color: #51a351;
}
.btn-group.open .btn-info.dropdown-toggle {
background-color: #2f96b4;
}
.btn-group.open .btn-inverse.dropdown-toggle {
background-color: #222222;
}
.btn .caret {
margin-top: 7px;
margin-left: 0;
}
.btn:hover .caret,
.open.btn-group .caret {
opacity: 1;
filter: alpha(opacity=100);
}
.btn-mini .caret {
margin-top: 5px;
}
.btn-small .caret {
margin-top: 6px;
}
.btn-large .caret {
margin-top: 6px;
border-left-width: 5px;
border-right-width: 5px;
border-top-width: 5px;
}
.dropup .btn-large .caret {
border-bottom: 5px solid #000000;
border-top: 0;
}
.btn-primary .caret,
.btn-warning .caret,
.btn-danger .caret,
.btn-info .caret,
.btn-success .caret,
.btn-inverse .caret {
border-top-color: #ffffff;
border-bottom-color: #ffffff;
opacity: 0.75;
filter: alpha(opacity=75);
}
.dropup,
.dropdown {
position: relative;
}
.dropdown-toggle {
*margin-bottom: -3px;
}
.dropdown-toggle:active,
.open .dropdown-toggle {
outline: 0;
}
.caret {
display: inline-block;
width: 0;
height: 0;
vertical-align: top;
border-top: 4px solid #000000;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
content: "";
opacity: 0.3;
filter: alpha(opacity=30);
}
.dropdown .caret {
margin-top: 8px;
margin-left: 2px;
}
.dropdown:hover .caret,
.open .caret {
opacity: 1;
filter: alpha(opacity=100);
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 160px;
padding: 4px 0;
margin: 1px 0 0;
list-style: none;
background-color: #ffffff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
*border-right-width: 2px;
*border-bottom-width: 2px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
.dropdown-menu.pull-right {
right: 0;
left: auto;
}
.dropdown-menu .divider {
*width: 100%;
height: 1px;
margin: 8px 1px;
*margin: -5px 0 5px;
overflow: hidden;
background-color: #e5e5e5;
border-bottom: 1px solid #ffffff;
}
.dropdown-menu a {
display: block;
padding: 3px 15px;
clear: both;
font-weight: normal;
line-height: 18px;
color: #333333;
white-space: nowrap;
}
.dropdown-menu li > a:hover,
.dropdown-menu .active > a,
.dropdown-menu .active > a:hover {
color: #ffffff;
text-decoration: none;
background-color: #0088cc;
}
.open {
*z-index: 1000;
}
.open > .dropdown-menu {
display: block;
}
.pull-right > .dropdown-menu {
right: 0;
left: auto;
}
.dropup .caret,
.navbar-fixed-bottom .dropdown .caret {
border-top: 0;
border-bottom: 4px solid #000000;
content: "\2191";
}
.dropup .dropdown-menu,
.navbar-fixed-bottom .dropdown .dropdown-menu {
top: auto;
bottom: 100%;
margin-bottom: 1px;
}
.typeahead {
margin-top: 2px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}

32
formulaires/inc-editer_liens_actions_roles.html

@ -0,0 +1,32 @@
#SET{presents,#ENV{id}|roles_presents_sur_id{#ENV{objet_source},#ENV{objet},#ENV{id_objet},#ENV{_objet_lien}}}
<div class="btn-group">
<a data-toggle="dropdown" href="#" title="Modifier les rôles" class="btn btn-mini dropdown-toggle">Modifier
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<B_roles_presents>
<li class='dropdown-header'>Enlever un rôle</li>
<BOUCLE_roles_presents(DATA){source table,#ENV{roles/roles/choix}}{valeur IN #GET{presents}}>
<li>
<button name="supprimer_lien\[#ENV{cle}-#VALEUR\]" class="link btn btn-mini delete">
[(#CHEMIN_IMAGE{supprimer-8.png}|balise_img{'X'})] [(#VALEUR|role{#ENV{table_source}})]
</button>
</li>
</BOUCLE_roles_presents>
<li class="divider"></li>
</B_roles_presents>
<B_roles_absents>
<li class='dropdown-header'>Ajouter un rôle</li>
<BOUCLE_roles_absents(DATA){source table,#ENV{roles/roles/choix}}{valeur !IN #GET{presents}}>
<li>
<button name="ajouter_lien\[#ENV{cle}-#VALEUR\]" class="link btn btn-mini delete">
[(#CHEMIN_IMAGE{inserer-8.png}|balise_img{'+'})] [(#VALEUR|role{#ENV{table_source}})]
</button>
</li>
</BOUCLE_roles_absents>
</li>
</ul>
</div>

9
formulaires/inc-editer_liens_roles.html

@ -0,0 +1,9 @@
#SET{exclus,#ENV{id}|roles_presents_sur_id{#ENV{objet_source},#ENV{objet},#ENV{id_objet},#ENV{_objet_lien}}}
<B_roles>
<select class="selection_roles" name="definir_roles\[#ENV{cle}\]\[\]"
multiple="multiple" data-placeholder="<:roles:choisir_role|attribut_html:>">
<BOUCLE_roles(DATA){source table,#ENV{roles/roles/choix}}{valeur !IN #GET{exclus}}>
<option value="#VALEUR">[(#VALEUR|role{#ENV{table_source}})]</option>
</BOUCLE_roles>
</select>
</B_roles>

319
inc/roles.php

@ -0,0 +1,319 @@
<?php
/**
* Plugin Rôles
* (c) 2012 Marcillaud Matthieu
* Licence GNU/GPL
*
* Gestion des rôles
*
* Les rôles sont une qualification précise sur une liaison entre
* deux objets. Ils doivent être définis dans la déclaration d'un objet
* pour être utilisés. Ils s'appliquent sur une colonne particulière
* de la table de liaison, par défaut 'role'.
*
* Cette table de liaison, lorsqu'elle a des rôles n'a plus sa clé primaire
* sur le couple (id_x, objet, id_objet) mais sur (id_x, objet, id_objet, colonne_role)
* de sorte qu'il peut exister plusieurs liens entre 2 objets, mais avec
* des rôles différents. Chaque ligne de la table lien correspond alors à
* un des rôles.
*/
if (!defined('_ECRIRE_INC_VERSION')) return;
/**
* Vérifie qu'un objet dispose de rôles fonctionnels
*
* Retourne une description des rôles si c'est le cas
*
* @param string $objet
* Objet source qui possède la table de liaison
* @param string $objet_destination
* Objet sur quoi on veut lier
* Si défini, le retour ne contient que les roles possibles pour cet objet
* Sinon retourne tous les roles possibles quelque soit l'objet
* @return bool|array
* false si rôles indisponibles on non déclarés
* array : description des roles applicables dans 3 index : colonne, titres, roles
**/
function roles_presents($objet, $objet_destination='') {
$desc = lister_tables_objets_sql(table_objet_sql($objet));
// pas de liste de roles, on sort
if (!isset($desc['roles_titres']) OR !($titres = $desc['roles_titres'])) {
return false;
}
// on vérifie que la table de liaison existe
include_spip('action/editer_liens');
if (!$lien = objet_associable($objet)) {
return false;
}
// on cherche ensuite si la colonne existe bien dans la table de liaison (par défaut 'role')
$colonne = isset($desc['roles_colonne']) ? $desc['roles_colonne'] : 'role';
$trouver_table = charger_fonction('trouver_table', 'base');
list(,$table_lien) = $lien;
$desc_lien = $trouver_table($table_lien);
if (!isset($desc_lien['field'][$colonne])) {
return false;
}
// sur quoi peuvent s'appliquer nos rôles
if (!$application = $desc['roles_objets']) {
return false;
}
// destination presente, on restreint si possible
if ($objet_destination) {
$objet_destination = table_objet($objet_destination);
// pour l'objet
if (isset($application[$objet_destination])) {
$application = $application[$objet_destination];
// sinon pour tous les objets
} elseif (isset($application['*'])) {
$application = $application['*'];
}
// sinon tant pis
else {
return false;
}
}
// tout est ok
return array(
'titres' => $titres,
'roles' => $application,
'colonne' => $colonne
);
}
/**
* Retrouve la colonne de liaison d'un rôle si définie entre 2 objets
*
* @param string $objet
* Objet source qui possède la table de liaison
* @param string $objet_destination
* Objet sur quoi on veut lier
* @return string
* Nom de la colonne, sinon vide
**/
function roles_colonne($objet, $objet_destination) {
if ($roles = roles_presents($objet, $objet_destination)) {
return $roles['colonne_role'];
}
return '';
}
/**
* Extrait le rôle et la colonne de role d'un tableau de qualification
*
* Calcule également une condition where pour ce rôle.
*
* Pour un objet pouvant recevoir des roles sur sa liaison avec un autre objet,
* on retrouve le rôle en question dans le tableau de qualification.
* Si le rôle n'est pas défini dedans, on prend le rôle par défaut
* déclaré.
*
* @param string $objet Objet source de la liaison
* @param string $objet_destination Objet de destination de la liaison
* @param array $qualif tableau de qualifications array(champ => valeur)
* @return array
* Liste (role, colonne, (array)condition) si role possible
* Liste ('', '', array()) sinon.
**/
function roles_trouver_dans_qualif($objet, $objet_destination, $qualif = array()) {
// si des rôles sont possibles, on les utilise
$role = $colonne_role = ''; # role défini
// condition du where par defaut
$cond = array();
if ($roles = roles_presents($objet, $objet_destination)) {
$colonne_role = $roles['colonne'];
// qu'il n'est pas défini
if (!isset($qualif[$colonne_role])
OR !($role = $qualif[$colonne_role])) {
$role = $roles['roles']['defaut'];
}
// where
$cond = array("$colonne_role=" . sql_quote($role));
}
return array($role, $colonne_role, $cond);
}
/**
* Gérer l'ajout dans la condition where du rôle
*
* On ajoute la condition uniquement si la liaison entre les 2 objets a une colonne de rôle !
*
* @param string $objet_source Objet source (qui possède la table de liens)
* @param string $objet Objet de destination
* @param array $cond
* Tableau de conditions where
* qui peut avoir un index spécial 'role' définissant le role à appliquer
* ou valant '*' pour tous les roles.
* @param bool $tous_si_absent
* true pour ne pas appliquer une condition sur le rôle s'il n'est pas indiqué
* dans la liste des conditions entrantes. Autrement dit, on n'applique
* pas de rôle par défaut si aucun n'est défini.
* @return array
* Liste (Tableau de conditions where complété du role, Colonne du role, role utilisé)
**/
function roles_creer_condition_role($objet_source, $objet, $cond, $tous_si_absent = false) {
// role par défaut, colonne
list($role_defaut, $colonne_role) = roles_trouver_dans_qualif($objet_source, $objet);
// chercher d'eventuels rôles transmis
$role = isset($cond['role']) ? $cond['role'] : ($tous_si_absent ? '' : $role_defaut);
unset($cond['role']); // cette condition est particuliere...
if ($colonne_role and $role) {
// on ajoute la condition du role aux autres conditions.
if ($role != '*') {
$cond[] = "$colonne_role=" .sql_quote($role);
}
}
return array($cond, $colonne_role, $role);
}
/**
* Liste des identifiants dont on ne peut ajouter de rôle
*
* Lister les id objet_source associés à l'objet id_objet
* via la table de lien objet_lien, et détermine dans cette liste
* lesquels ont les rôles complets, c'est à dire qu'on ne peut leur
* affecteur d'autres rôles parmi ceux qui existe pour cette liaison.
*
* @see lister_objets_lies()
*
* @param string $objet_source Objet dont on veut récupérer la liste des identifiants
* @param string $objet Objet sur lequel est liée la source
* @param int $id_objet Identifiant d'objet sur lequel est liée la source
* @param string $objet_lien Objet dont on utilise la table de liaison (c'est forcément soit $objet_source, soit $objet)
* @return array Liste des identifiants
*/
function roles_complets($objet_source, $objet, $id_objet, $objet_lien) {
$presents = roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien);
// pas de roles sur ces objets => la liste par defaut, comme sans role
if ($presents === false) {
return lister_objets_lies($objet_source, $objet, $id_objet, $objet_lien);
}
// types de roles possibles
$roles_possibles = $presents['roles']['roles']['choix'];
// couples id / roles
$ids = $presents['ids'];
// pour chaque groupe, on fait le diff entre tous les roles possibles
// et les roles attribués à l'élément : s'il en reste, c'est que l'élément
// n'est pas complet
$complets = array();
foreach ($ids as $id => $roles_presents) {
if (!array_diff($roles_possibles, $roles_presents)) {
$complets[] = $id;
}
}
return $complets;
}
/**
* Liste les roles attribués entre 2 objets/id_objet sur une table de liaison donnée
*
* @param string $id_objet_source Identifiant de l'objet qu'on lie
* @param string $objet_source Objet qu'on lie
* @param string $objet Objet sur lequel est liée la source
* @param int $id_objet Identifiant d'objet sur lequel est liée la source
* @param string $objet_lien Objet dont on utilise la table de liaison (c'est forcément soit $objet_source, soit $objet)
* @return array Liste des roles
*/
function roles_presents_sur_id($id_objet_source, $objet_source, $objet, $id_objet, $objet_lien) {
$presents = roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien);
// pas de roles sur ces objets => la liste par defaut, comme sans role
if ($presents === false) {
return array();
}
if (!isset($presents['ids'][$id_objet_source])) {
return array();
}
return $presents['ids'][$id_objet_source];
}
/**
* Lister des rôles présents sur une liaion, pour un objet sur un autre,
* classés par identifiant de l'objet
*
* Lister les id objet_source associés à l'objet id_objet
* via la table de lien objet_lien, et groupe cette liste
* par identifiant (la clé) et ses roles attribués (tableau de valeur)
*
* On retourne cette liste dans l'index 'ids' et la description des roles
* pour la liaison dans l'index 'roles' pour éviter le le faire recalculer
* aux fonctions utilisant celle ci.
*
* @param string $objet_source Objet dont on veut récupérer la liste des identifiants
* @param string $objet Objet sur lequel est liée la source
* @param int $id_objet Identifiant d'objet sur lequel est liée la source
* @param string $objet_lien Objet dont on utilise la table de liaison (c'est forcément soit $objet_source, soit $objet)
* @return array|bool
* - Tableau d'index
* - roles : tableau de description des roles,
* - ids : tableau des identifiants / roles.
* - False si pas de role déclarés
*/
function roles_presents_liaisons($objet_source, $objet, $id_objet, $objet_lien) {
static $done = array();
// stocker le résultat
$hash = md5($objet_source . $objet . $id_objet . $objet_lien);
if (isset($done[$hash])) {
return $done[$hash];
}
// pas de roles sur ces objets, on sort
$roles = roles_presents($objet_lien, ($objet_lien==$objet) ? $objet_source : $objet);
if (!$roles) {
return $done[$hash] = false;
}
// inspiré de lister_objets_lies()
if ($objet_lien==$objet){
$res = objet_trouver_liens(array($objet=>$id_objet),array($objet_source=>'*'));
}
else{
$res = objet_trouver_liens(array($objet_source=>'*'),array($objet=>$id_objet));
}
// types de roles possibles
$roles_possibles = $roles['roles']['choix'];
// colonne du role
$colonne = $roles['colonne'];
// on recupere par id, et role existant
$ids = array();
while ($row = array_shift($res)) {
$id = $row[$objet_source];
if (!isset($ids[$id])) {
$ids[$id] = array();
}
// tableau des roles présents
$ids[$id][] = $row[$colonne];
}
return $done[$hash] = array(
'roles' => $roles,
'ids' => $ids
);
}

100
javascript/bootstrap-dropdown.js vendored

@ -0,0 +1,100 @@
/* ============================================================
* bootstrap-dropdown.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* DROPDOWN CLASS DEFINITION
* ========================= */
var toggle = '[data-toggle="dropdown"]'
, Dropdown = function (element) {
var $el = $(element).on('click.dropdown.data-api', this.toggle)
$('html').on('click.dropdown.data-api', function () {
$el.parent().removeClass('open')
})
}
Dropdown.prototype = {
constructor: Dropdown
, toggle: function (e) {
var $this = $(this)
, $parent
, selector
, isActive
if ($this.is('.disabled, :disabled')) return
selector = $this.attr('data-target')
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
isActive = $parent.hasClass('open')
clearMenus()
if (!isActive) $parent.toggleClass('open')
return false
}
}
function clearMenus() {
$(toggle).parent().removeClass('open')
}
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
$.fn.dropdown = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('dropdown')
if (!data) $this.data('dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.dropdown.Constructor = Dropdown
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
$('body')
.on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
.on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
})
}(window.jQuery);

9
javascript/roles.js

@ -0,0 +1,9 @@
;(function ($) {
jQuery(document).ready(function(){
spip_chosen = function() {
$("select.selection_roles").chosen();
}
spip_chosen();
onAjaxLoad(spip_chosen);
});
})(jQuery);

14
lang/paquet-roles_fr.php

@ -0,0 +1,14 @@
<?php
// This is a SPIP language file -- Ceci est un fichier langue de SPIP
if (!defined('_ECRIRE_INC_VERSION')) return;
$GLOBALS[$GLOBALS['idx_lang']] = array(
// R
'roles_description' => 'Ce plugin sert d\'API pour définir des rôles sur des tables de liaisons.',
'roles_nom' => 'Rôles',
'roles_slogan' => 'Typer des liaisons',
);
?>

15
lang/roles_fr.php

@ -0,0 +1,15 @@
<?php
// This is a SPIP language file -- Ceci est un fichier langue de SPIP
if (!defined('_ECRIRE_INC_VERSION')) return;
$GLOBALS[$GLOBALS['idx_lang']] = array(
// C
'choisir_role' => 'Choisir…',
// R
'roles_titre' => 'Rôles',
'role_titre' => 'Rôle',
);
?>

BIN
lib/chosen/chosen-sprite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

396
lib/chosen/chosen.css

@ -0,0 +1,396 @@
/* @group Base */
.chzn-container {
font-size: 13px;
position: relative;
display: inline-block;
zoom: 1;
*display: inline;
}
.chzn-container .chzn-drop {
background: #fff;
border: 1px solid #aaa;
border-top: 0;
position: absolute;
top: 29px;
left: 0;
-webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15);
-moz-box-shadow : 0 4px 5px rgba(0,0,0,.15);
-o-box-shadow : 0 4px 5px rgba(0,0,0,.15);
box-shadow : 0 4px 5px rgba(0,0,0,.15);
z-index: 1010;
}
/* @end */
/* @group Single Chosen */
.chzn-container-single .chzn-single {
background-color: #ffffff;
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 );
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
background-image: -ms-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
background-image: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
-webkit-border-radius: 5px;
-moz-border-radius : 5px;
border-radius : 5px;
-moz-background-clip : padding;
-webkit-background-clip: padding-box;
background-clip : padding-box;
border: 1px solid #aaaaaa;
-webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
-moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
display: block;
overflow: hidden;
white-space: nowrap;
position: relative;
height: 23px;
line-height: 24px;
padding: 0 0 0 8px;
color: #444444;
text-decoration: none;
}
.chzn-container-single .chzn-default {
color: #999;
}
.chzn-container-single .chzn-single span {
margin-right: 26px;
display: block;
overflow: hidden;
white-space: nowrap;
-o-text-overflow: ellipsis;
-ms-text-overflow: ellipsis;
text-overflow: ellipsis;
}
.chzn-container-single .chzn-single abbr {
display: block;
position: absolute;
right: 26px;
top: 6px;
width: 12px;
height: 13px;
font-size: 1px;
background: url('chosen-sprite.png') right top no-repeat;
}
.chzn-container-single .chzn-single abbr:hover {
background-position: right -11px;
}
.chzn-container-single.chzn-disabled .chzn-single abbr:hover {
background-position: right top;
}
.chzn-container-single .chzn-single div {
position: absolute;
right: 0;
top: 0;
display: block;
height: 100%;
width: 18px;
}
.chzn-container-single .chzn-single div b {
background: url('chosen-sprite.png') no-repeat 0 0;
display: block;
width: 100%;
height: 100%;
}
.chzn-container-single .chzn-search {
padding: 3px 4px;
position: relative;
margin: 0;
white-space: nowrap;
z-index: 1010;
}
.chzn-container-single .chzn-search input {
background: #fff url('chosen-sprite.png') no-repeat 100% -22px;
background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat 100% -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat 100% -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat 100% -22px, -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat 100% -22px, linear-gradient(top, #eeeeee 1%, #ffffff 15%);
margin: 1px 0;
padding: 4px 20px 4px 5px;
outline: 0;
border: 1px solid #aaa;
font-family: sans-serif;
font-size: 1em;
}
.chzn-container-single .chzn-drop {
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius : 0 0 4px 4px;
border-radius : 0 0 4px 4px;
-moz-background-clip : padding;
-webkit-background-clip: padding-box;
background-clip : padding-box;
}
/* @end */
.chzn-container-single-nosearch .chzn-search input {
position: absolute;
left: -9000px;
}
/* @group Multi Chosen */
.chzn-container-multi .chzn-choices {
background-color: #fff;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
border: 1px solid #aaa;
margin: 0;
padding: 0;
cursor: text;
overflow: hidden;
height: auto !important;
height: 1%;
position: relative;
}
.chzn-container-multi .chzn-choices li {
float: left;
list-style: none;
}
.chzn-container-multi .chzn-choices .search-field {
white-space: nowrap;
margin: 0;
padding: 0;
}
.chzn-container-multi .chzn-choices .search-field input {
color: #666;
background: transparent !important;
border: 0 !important;
font-family: sans-serif;
font-size: 100%;
height: 15px;
padding: 5px;
margin: 1px 0;
outline: 0;
-webkit-box-shadow: none;
-moz-box-shadow : none;
-o-box-shadow : none;
box-shadow : none;
}
.chzn-container-multi .chzn-choices .search-field .default {
color: #999;
}
.chzn-container-multi .chzn-choices .search-choice {
-webkit-border-radius: 3px;
-moz-border-radius : 3px;
border-radius : 3px;
-moz-background-clip : padding;
-webkit-background-clip: padding-box;
background-clip : padding-box;
background-color: #e4e4e4;
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
-webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
-moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
color: #333;
border: 1px solid #aaaaaa;
line-height: 13px;
padding: 3px 20px 3px 5px;
margin: 3px 0 3px 5px;
position: relative;