Added checks before publishing content
authorThierry Florac <thierry.florac@onf.fr>
Wed, 30 May 2018 11:23:14 +0200
changeset 565 070528d7f960
parent 564 36062350d768
child 566 3bd064283183
Added checks before publishing content
src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.mo
src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po
src/pyams_content/locales/pyams_content.pot
src/pyams_content/shared/common/interfaces/__init__.py
src/pyams_content/shared/common/security.py
src/pyams_content/shared/common/zmi/security.py
src/pyams_content/shared/common/zmi/templates/check-input.pt
src/pyams_content/shared/common/zmi/templates/preview-input.pt
src/pyams_content/shared/common/zmi/workflow.py
Binary file src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.mo has changed
--- a/src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po	Tue May 29 09:51:54 2018 +0200
+++ b/src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po	Wed May 30 11:23:14 2018 +0200
@@ -5,7 +5,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-05-24 09:43+0200\n"
+"POT-Creation-Date: 2018-05-30 11:11+0200\n"
 "PO-Revision-Date: 2015-09-10 10:42+0200\n"
 "Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
 "Language-Team: French\n"
@@ -81,7 +81,7 @@
 msgid "Medias gallery"
 msgstr "Galerie de médias"
 
-#: src/pyams_content/component/gallery/__init__.py:159
+#: src/pyams_content/component/gallery/__init__.py:154
 msgid "Gallery"
 msgstr "Galerie de médias"
 
@@ -321,8 +321,8 @@
 
 #: src/pyams_content/component/extfile/__init__.py:255
 #: src/pyams_content/component/extfile/__init__.py:260
-#: src/pyams_content/component/paragraph/video.py:49
-#: src/pyams_content/component/paragraph/video.py:62
+#: src/pyams_content/component/paragraph/video.py:51
+#: src/pyams_content/component/paragraph/video.py:64
 msgid "Video"
 msgstr "Vidéo"
 
@@ -479,7 +479,7 @@
 
 #: src/pyams_content/component/illustration/paragraph.py:40
 #: src/pyams_content/component/illustration/paragraph.py:47
-#: src/pyams_content/component/illustration/__init__.py:134
+#: src/pyams_content/component/illustration/__init__.py:132
 #: src/pyams_content/component/illustration/zmi/__init__.py:54
 #: src/pyams_content/component/illustration/zmi/__init__.py:81
 msgid "Illustration"
@@ -516,13 +516,13 @@
 "><strong>ATTENTION :</strong> certains modes de rendu ne prennent pas en "
 "compte tous les types de médias !</span>"
 
-#: src/pyams_content/component/paragraph/milestone.py:205
-#: src/pyams_content/component/paragraph/milestone.py:227
+#: src/pyams_content/component/paragraph/milestone.py:199
+#: src/pyams_content/component/paragraph/milestone.py:222
 #: src/pyams_content/component/paragraph/zmi/milestone.py:297
 msgid "Milestones"
 msgstr "Chronologie"
 
-#: src/pyams_content/component/paragraph/milestone.py:236
+#: src/pyams_content/component/paragraph/milestone.py:231
 msgid "Milestones paragraph"
 msgstr "Chronologie"
 
@@ -534,12 +534,12 @@
 msgid "Selected paragraph is not visible"
 msgstr "le bloc sélectionné n'est pas visible"
 
-#: src/pyams_content/component/paragraph/keypoint.py:44
+#: src/pyams_content/component/paragraph/keypoint.py:46
 #: src/pyams_content/component/paragraph/interfaces/keypoint.py:39
 msgid "Key points"
 msgstr "Points clés"
 
-#: src/pyams_content/component/paragraph/keypoint.py:59
+#: src/pyams_content/component/paragraph/keypoint.py:61
 msgid "Key points paragraph"
 msgstr "Points clés"
 
@@ -551,13 +551,13 @@
 msgid "no visible paragraph"
 msgstr "aucun bloc de contenu visible"
 
-#: src/pyams_content/component/paragraph/pictogram.py:195
-#: src/pyams_content/component/paragraph/pictogram.py:217
+#: src/pyams_content/component/paragraph/pictogram.py:189
+#: src/pyams_content/component/paragraph/pictogram.py:212
 #: src/pyams_content/component/paragraph/zmi/pictogram.py:301
 msgid "Pictograms"
 msgstr "Pictogrammes"
 
-#: src/pyams_content/component/paragraph/pictogram.py:226
+#: src/pyams_content/component/paragraph/pictogram.py:221
 msgid "Pictograms paragraph"
 msgstr "Pictogrammes"
 
@@ -565,61 +565,61 @@
 msgid "Selected pictogram is missing"
 msgstr "le pictogramme sélectionné est introuvable"
 
-#: src/pyams_content/component/paragraph/keynumber.py:189
-#: src/pyams_content/component/paragraph/keynumber.py:212
+#: src/pyams_content/component/paragraph/keynumber.py:183
+#: src/pyams_content/component/paragraph/keynumber.py:207
 #: src/pyams_content/component/paragraph/zmi/keynumber.py:276
 msgid "Key numbers"
 msgstr "Chiffres-clés"
 
-#: src/pyams_content/component/paragraph/keynumber.py:221
+#: src/pyams_content/component/paragraph/keynumber.py:216
 msgid "Key numbers paragraph"
 msgstr "Chiffres-clés"
 
-#: src/pyams_content/component/paragraph/frame.py:50
+#: src/pyams_content/component/paragraph/frame.py:52
 msgid "Framed text"
 msgstr "Encadré"
 
-#: src/pyams_content/component/paragraph/frame.py:60
+#: src/pyams_content/component/paragraph/frame.py:62
 msgid "Framed text paragraph"
 msgstr "Encadré"
 
-#: src/pyams_content/component/paragraph/verbatim.py:48
+#: src/pyams_content/component/paragraph/verbatim.py:50
 msgid "Verbatim"
 msgstr "Verbatim"
 
-#: src/pyams_content/component/paragraph/verbatim.py:60
+#: src/pyams_content/component/paragraph/verbatim.py:62
 msgid "Verbatim paragraph"
 msgstr "Verbatim"
 
-#: src/pyams_content/component/paragraph/html.py:61
+#: src/pyams_content/component/paragraph/html.py:63
 msgid "Raw HTML "
 msgstr "Code HTML"
 
-#: src/pyams_content/component/paragraph/html.py:71
+#: src/pyams_content/component/paragraph/html.py:73
 msgid "Raw HTML paragraph"
 msgstr "Code HTML"
 
-#: src/pyams_content/component/paragraph/html.py:117
+#: src/pyams_content/component/paragraph/html.py:119
 msgid "Rich text"
 msgstr "Texte enrichi"
 
-#: src/pyams_content/component/paragraph/html.py:127
+#: src/pyams_content/component/paragraph/html.py:129
 msgid "Rich text paragraph"
 msgstr "Texte enrichi"
 
-#: src/pyams_content/component/paragraph/contact.py:45
-#: src/pyams_content/component/paragraph/contact.py:74
+#: src/pyams_content/component/paragraph/contact.py:47
+#: src/pyams_content/component/paragraph/contact.py:76
 msgid "Contact card"
 msgstr "Fiche contact"
 
-#: src/pyams_content/component/paragraph/header.py:44
+#: src/pyams_content/component/paragraph/header.py:46
 #: src/pyams_content/component/paragraph/interfaces/header.py:39
 #: src/pyams_content/features/alert/interfaces.py:65
 #: src/pyams_content/features/alert/zmi/container.py:158
 msgid "Header"
 msgstr "Chapô"
 
-#: src/pyams_content/component/paragraph/header.py:59
+#: src/pyams_content/component/paragraph/header.py:61
 msgid "Header paragraph"
 msgstr "Chapô"
 
@@ -700,35 +700,36 @@
 "REMARQUE : supprimer des types de la liste des types de blocs autorisés sera "
 "sans effet sur les contenus existants."
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:208
+#: src/pyams_content/component/paragraph/zmi/__init__.py:214
+#: src/pyams_content/shared/common/zmi/templates/preview-input.pt:39
 #: src/pyams_content/features/preview/zmi/__init__.py:45
 msgid "Preview"
 msgstr "Aperçu"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:213
-#: src/pyams_content/shared/common/zmi/workflow.py:115
-#: src/pyams_content/shared/common/zmi/workflow.py:207
-#: src/pyams_content/shared/common/zmi/workflow.py:252
-#: src/pyams_content/shared/common/zmi/workflow.py:311
-#: src/pyams_content/shared/common/zmi/workflow.py:405
-#: src/pyams_content/shared/common/zmi/workflow.py:466
-#: src/pyams_content/shared/common/zmi/workflow.py:511
-#: src/pyams_content/shared/common/zmi/workflow.py:557
-#: src/pyams_content/shared/common/zmi/workflow.py:605
-#: src/pyams_content/shared/common/zmi/workflow.py:650
-#: src/pyams_content/shared/common/zmi/workflow.py:696
-#: src/pyams_content/shared/common/zmi/workflow.py:752
+#: src/pyams_content/component/paragraph/zmi/__init__.py:219
+#: src/pyams_content/shared/common/zmi/workflow.py:125
+#: src/pyams_content/shared/common/zmi/workflow.py:217
+#: src/pyams_content/shared/common/zmi/workflow.py:262
+#: src/pyams_content/shared/common/zmi/workflow.py:321
+#: src/pyams_content/shared/common/zmi/workflow.py:415
+#: src/pyams_content/shared/common/zmi/workflow.py:476
+#: src/pyams_content/shared/common/zmi/workflow.py:521
+#: src/pyams_content/shared/common/zmi/workflow.py:567
+#: src/pyams_content/shared/common/zmi/workflow.py:615
+#: src/pyams_content/shared/common/zmi/workflow.py:660
+#: src/pyams_content/shared/common/zmi/workflow.py:706
+#: src/pyams_content/shared/common/zmi/workflow.py:762
 #: src/pyams_content/shared/common/zmi/__init__.py:276
 #: src/pyams_content/shared/common/zmi/owner.py:74
 #: src/pyams_content/features/review/zmi/__init__.py:90
 msgid "Cancel"
 msgstr "Annuler"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:215
+#: src/pyams_content/component/paragraph/zmi/__init__.py:221
 msgid "Submit"
 msgstr "Enregistrer"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:200
+#: src/pyams_content/component/paragraph/zmi/__init__.py:202
 msgid "Paragraph was correctly added."
 msgstr "Le bloc a été ajouté."
 
@@ -1236,13 +1237,13 @@
 msgid "Presentation template used for this header"
 msgstr "Mode de rendu utilisé par ce chapô"
 
-#: src/pyams_content/component/theme/__init__.py:81
+#: src/pyams_content/component/theme/__init__.py:65
 #: src/pyams_content/component/theme/zmi/portlet.py:40
 #: src/pyams_content/component/theme/interfaces/__init__.py:43
 msgid "Themes"
 msgstr "Thèmes"
 
-#: src/pyams_content/component/theme/__init__.py:90
+#: src/pyams_content/component/theme/__init__.py:74
 msgid "no defined theme"
 msgstr "aucun thème défini"
 
@@ -1263,8 +1264,8 @@
 msgid "Selected themes"
 msgstr "Thèmes sélectionnés"
 
-#: src/pyams_content/component/association/paragraph.py:46
-#: src/pyams_content/component/association/paragraph.py:55
+#: src/pyams_content/component/association/paragraph.py:48
+#: src/pyams_content/component/association/paragraph.py:57
 msgid "Associations paragraph"
 msgstr "Liens et pièces jointes"
 
@@ -1443,9 +1444,9 @@
 "Nom de la boîte aux lettres, tel qu'il sera affiché dans l'application de "
 "messagerie."
 
-#: src/pyams_content/component/video/paragraph.py:45
-#: src/pyams_content/component/video/paragraph.py:55
-#: src/pyams_content/component/video/__init__.py:80
+#: src/pyams_content/component/video/paragraph.py:47
+#: src/pyams_content/component/video/paragraph.py:57
+#: src/pyams_content/component/video/__init__.py:73
 msgid "External video"
 msgstr "Vidéo externe"
 
@@ -1875,50 +1876,54 @@
 msgid "Click to see subtypes"
 msgstr "Montrer ou caher les sous-types"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:116
+#: src/pyams_content/shared/common/zmi/workflow.py:907
+msgid "Prior checks"
+msgstr "Contrôles préalables : avez-vous ?"
+
+#: src/pyams_content/shared/common/zmi/workflow.py:126
 msgid "Request publication"
 msgstr "Demander la publication"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:208
+#: src/pyams_content/shared/common/zmi/workflow.py:218
 #: src/pyams_content/workflow/__init__.py:315
 msgid "Cancel publication request"
 msgstr "Annuler la demande de publication"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:253
+#: src/pyams_content/shared/common/zmi/workflow.py:263
 msgid "Refuse publication request"
 msgstr "Refuser la demande de publication"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:312
+#: src/pyams_content/shared/common/zmi/workflow.py:322
 #: src/pyams_content/workflow/basic.py:196
 msgid "Publish"
 msgstr "Publier"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:406
+#: src/pyams_content/shared/common/zmi/workflow.py:416
 msgid "Request retire"
 msgstr "Demander le retrait"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:467
+#: src/pyams_content/shared/common/zmi/workflow.py:477
 msgid "Cancel retire request"
 msgstr "Annuler la demande de retrait"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:512
+#: src/pyams_content/shared/common/zmi/workflow.py:522
 msgid "Retire"
 msgstr "Retirer"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:558
+#: src/pyams_content/shared/common/zmi/workflow.py:568
 #: src/pyams_content/workflow/__init__.py:436
 msgid "Request archive"
 msgstr "Demander l'archivage"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:606
+#: src/pyams_content/shared/common/zmi/workflow.py:616
 msgid "Cancel archive request"
 msgstr "Annuler la demande d'archivage"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:651
+#: src/pyams_content/shared/common/zmi/workflow.py:661
 msgid "Archive"
 msgstr "Archiver"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:697
+#: src/pyams_content/shared/common/zmi/workflow.py:707
 #: src/pyams_content/workflow/__init__.py:501
 #: src/pyams_content/workflow/__init__.py:513
 #: src/pyams_content/workflow/__init__.py:525
@@ -1929,36 +1934,52 @@
 msgid "Create new version"
 msgstr "Créer une nouvelle version"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:753
+#: src/pyams_content/shared/common/zmi/workflow.py:763
 #: src/pyams_content/workflow/__init__.py:561
 #: src/pyams_content/workflow/basic.py:248
 msgid "Delete version"
 msgstr "Supprimer cette version"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:178
-#: src/pyams_content/shared/common/zmi/workflow.py:375
+#: src/pyams_content/shared/common/zmi/workflow.py:853
+msgid "Previewed content?"
+msgstr "Prévisualisé ce contenu ?"
+
+#: src/pyams_content/shared/common/zmi/workflow.py:857
+msgid "Verified content?"
+msgstr "Audité ce contenu ?"
+
+#: src/pyams_content/shared/common/zmi/workflow.py:188
+#: src/pyams_content/shared/common/zmi/workflow.py:385
 msgid "Publication start date is required"
 msgstr "La date de début de publication est obligatoire"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:281
-#: src/pyams_content/shared/common/zmi/workflow.py:437
+#: src/pyams_content/shared/common/zmi/workflow.py:291
+#: src/pyams_content/shared/common/zmi/workflow.py:447
 msgid "A comment is required"
 msgstr "Le commentaire est obligatoire"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:776
+#: src/pyams_content/shared/common/zmi/workflow.py:786
 msgid "Delete content"
 msgstr "Supprimer définitivement ce contenu"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:785
+#: src/pyams_content/shared/common/zmi/workflow.py:795
 msgid "Delete definitively"
 msgstr "Supprimer définitivement"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:80
+#: src/pyams_content/shared/common/zmi/workflow.py:920
+msgid ""
+"You must confirm that you previewed and checked this content before "
+"requesting publication!!"
+msgstr ""
+"Vous devez avoir prévisualisé et audité ce contenu avant de pouvoir le "
+"publier !!"
+
+#: src/pyams_content/shared/common/zmi/workflow.py:90
 #, python-format
 msgid "{state} | by {principal}"
 msgstr "{state} | par {principal}"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:76
+#: src/pyams_content/shared/common/zmi/workflow.py:86
 #: src/pyams_content/workflow/__init__.py:648
 #: src/pyams_content/workflow/__init__.py:619
 #: src/pyams_content/workflow/basic.py:315
@@ -2400,36 +2421,64 @@
 msgid "Content publication start date is not passed yet"
 msgstr "La date de début de publication n'est pas encore atteinte"
 
-#: src/pyams_content/shared/common/zmi/security.py:61
+#: src/pyams_content/shared/common/zmi/security.py:65
+msgid "Contributors restrictions"
+msgstr "Paramètres des contributeurs"
+
+#: src/pyams_content/shared/common/zmi/security.py:74
+msgid "Content contributors restrictions"
+msgstr "Liste des contributeurs"
+
+#: src/pyams_content/shared/common/zmi/security.py:108
+msgid "Contributor name"
+msgstr "Nom du contributeur"
+
+#: src/pyams_content/shared/common/zmi/security.py:119
+#: src/pyams_content/shared/common/zmi/security.py:277
+#: src/pyams_content/shared/common/interfaces/__init__.py:252
+#: src/pyams_content/shared/common/interfaces/__init__.py:277
+msgid "Publication checks"
+msgstr "Activer le tunnel de publication"
+
+#: src/pyams_content/shared/common/zmi/security.py:225
 msgid "Managers restrictions"
-msgstr "Restrictions des responsables"
-
-#: src/pyams_content/shared/common/zmi/security.py:70
+msgstr "Paramètres des responsables"
+
+#: src/pyams_content/shared/common/zmi/security.py:234
 msgid "Content managers restrictions"
 msgstr "Liste des responsables"
 
-#: src/pyams_content/shared/common/zmi/security.py:102
+#: src/pyams_content/shared/common/zmi/security.py:266
 msgid "Manager name"
 msgstr "Nom du responsable"
 
-#: src/pyams_content/shared/common/zmi/security.py:112
+#: src/pyams_content/shared/common/zmi/security.py:294
 msgid "Restricted"
 msgstr "Restrictions"
 
-#: src/pyams_content/shared/common/zmi/security.py:129
+#: src/pyams_content/shared/common/zmi/security.py:311
 msgid "Owners"
 msgstr "Propriétaires"
 
-#: src/pyams_content/shared/common/zmi/security.py:178
+#: src/pyams_content/shared/common/zmi/security.py:401
+msgid "Publication workflow"
+msgstr "Workflow de publication"
+
+#: src/pyams_content/shared/common/zmi/security.py:168
+#, python-format
+msgid "Edit contributor restrictions for « {0} »"
+msgstr "Gérer les paramètres d'intervention de « {0} »"
+
+#: src/pyams_content/shared/common/zmi/security.py:360
 #, python-format
 msgid "Edit manager restrictions for « {0} »"
-msgstr "Gérer le périmètre d'intervention de « {0} »"
-
-#: src/pyams_content/shared/common/zmi/security.py:220
+msgstr "Gérer les paramètres d'intervention de « {0} »"
+
+#: src/pyams_content/shared/common/zmi/security.py:407
 msgid "Apply contents restrictions"
 msgstr "Appliquer des restrictions d'accès"
 
-#: src/pyams_content/shared/common/zmi/security.py:222
+#: src/pyams_content/shared/common/zmi/security.py:409
 msgid ""
 "You can specify which contents this manager will be able to manage. If you "
 "specify several criteria, the manager will be able to manage contents for "
@@ -2508,6 +2557,19 @@
 "actuel ; il pourra ensuite à nouveau être publié, en créant une nouvelle "
 "version."
 
+#: src/pyams_content/shared/common/zmi/templates/check-input.pt:34
+#: src/pyams_content/shared/common/zmi/templates/preview-input.pt:34
+#: src/pyams_content/shared/common/interfaces/types.py:39
+#: src/pyams_content/shared/form/zmi/field.py:159
+#: src/pyams_content/shared/form/interfaces/__init__.py:61
+msgid "Label"
+msgstr "Libellé"
+
+#: src/pyams_content/shared/common/zmi/templates/check-input.pt:39
+#: src/pyams_content/features/checker/zmi/__init__.py:43
+msgid "Audit"
+msgstr "Audit"
+
 #: src/pyams_content/shared/common/zmi/templates/dashboard.pt:28
 #: src/pyams_content/root/zmi/templates/dashboard.pt:28
 msgid "Quick search..."
@@ -2735,12 +2797,6 @@
 msgid "Name of this data type; must be unique between all data types"
 msgstr "Nom de ce type de donnée ; doit être unique entre tous les types"
 
-#: src/pyams_content/shared/common/interfaces/types.py:39
-#: src/pyams_content/shared/form/zmi/field.py:159
-#: src/pyams_content/shared/form/interfaces/__init__.py:61
-msgid "Label"
-msgstr "Libellé"
-
 #: src/pyams_content/shared/common/interfaces/types.py:42
 msgid "Navigation label"
 msgstr "Libellé de navigation"
@@ -3001,15 +3057,31 @@
 "Les invités sont autorisés à consulter des contenus dont l'accès a été "
 "restreint"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:216
+#: src/pyams_content/shared/common/interfaces/__init__.py:213
 msgid "Principal ID"
 msgstr "ID utilisateur"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:221
+#: src/pyams_content/shared/common/interfaces/__init__.py:253
+msgid ""
+"If 'yes', this contributor will have to confirm that contents have been "
+"previewed and checked before asking for publication"
+msgstr ""
+"Si 'oui', ce contributeur devra confirmer qu'il a bien prévisualisé et "
+"audité chaque contenu avant de pouvoir effectuer une demande de publication"
+
+#: src/pyams_content/shared/common/interfaces/__init__.py:278
+msgid ""
+"If 'yes', this manager will have to confirm that contents have been "
+"previewed and checked before publishing a content"
+msgstr ""
+"Si 'oui', ce responsable devra confirmer qu'il a bien prévisualisé et "
+"audité chaque contenu avant de pouvoir effectuer une publication"
+
+#: src/pyams_content/shared/common/interfaces/__init__.py:283
 msgid "Restricted contents"
 msgstr "Accès restreints"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:222
+#: src/pyams_content/shared/common/interfaces/__init__.py:284
 msgid ""
 "If 'yes', this manager will get restricted access to manage contents based "
 "on selected settings"
@@ -3017,11 +3089,11 @@
 "Si 'oui', ce responsable n'aura qu'un accès restreint à certains contenus en "
 "fonction de paramètres spécifiques"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:227
+#: src/pyams_content/shared/common/interfaces/__init__.py:289
 msgid "Selected owners"
 msgstr "Propriétaires"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:228
+#: src/pyams_content/shared/common/interfaces/__init__.py:290
 msgid "Manager will have access to contents owned by these principals"
 msgstr ""
 "Le responsable n'aura accès qu'aux contenus dont ces utilisateurs sont "
@@ -3035,51 +3107,51 @@
 msgid "no field defined"
 msgstr "aucun champ défini"
 
-#: src/pyams_content/shared/form/handler.py:85
+#: src/pyams_content/shared/form/handler.py:80
 msgid "Mailto form handler"
 msgstr "Envoi des données par mail"
 
-#: src/pyams_content/shared/form/handler.py:45
+#: src/pyams_content/shared/form/handler.py:44
 msgid "No selected handler..."
 msgstr "Aucun gestionnaire sélectionné"
 
-#: src/pyams_content/shared/form/field.py:147
+#: src/pyams_content/shared/form/field.py:137
 msgid "Text"
 msgstr "Texte simple"
 
-#: src/pyams_content/shared/form/field.py:157
+#: src/pyams_content/shared/form/field.py:147
 msgid "Multi-lines text"
 msgstr "Texte multi-lignes"
 
+#: src/pyams_content/shared/form/field.py:157
+msgid "Boolean"
+msgstr "Booléen"
+
 #: src/pyams_content/shared/form/field.py:167
-msgid "Boolean"
-msgstr "Booléen"
-
-#: src/pyams_content/shared/form/field.py:177
 msgid "Integer"
 msgstr "Nombre entier"
 
-#: src/pyams_content/shared/form/field.py:187
+#: src/pyams_content/shared/form/field.py:177
 msgid "Decimal"
 msgstr "Nombre décimal"
 
+#: src/pyams_content/shared/form/field.py:187
+msgid "E-mail address"
+msgstr "Adresse de messagerie"
+
 #: src/pyams_content/shared/form/field.py:197
-msgid "E-mail address"
-msgstr "Adresse de messagerie"
-
-#: src/pyams_content/shared/form/field.py:207
 msgid "URI"
 msgstr "URI"
 
-#: src/pyams_content/shared/form/field.py:217
+#: src/pyams_content/shared/form/field.py:207
 msgid "Date"
 msgstr "Date"
 
-#: src/pyams_content/shared/form/field.py:231
+#: src/pyams_content/shared/form/field.py:221
 msgid "Choice"
 msgstr "Choix unique dans une liste"
 
-#: src/pyams_content/shared/form/field.py:251
+#: src/pyams_content/shared/form/field.py:241
 msgid "List"
 msgstr "Choix multiples dans une liste"
 
@@ -3453,22 +3525,22 @@
 msgid "Other terms"
 msgstr "Autres thèmes"
 
-#: src/pyams_content/shared/imagemap/paragraph.py:45
-#: src/pyams_content/shared/imagemap/paragraph.py:58
+#: src/pyams_content/shared/imagemap/paragraph.py:47
+#: src/pyams_content/shared/imagemap/paragraph.py:60
 #: src/pyams_content/shared/imagemap/interfaces/__init__.py:35
 msgid "Image map"
 msgstr "Image cliquable"
 
-#: src/pyams_content/shared/imagemap/paragraph.py:89
+#: src/pyams_content/shared/imagemap/paragraph.py:91
 msgid "no selected image map"
 msgstr "aucune image cliquable sélectionnée"
 
-#: src/pyams_content/shared/imagemap/paragraph.py:95
+#: src/pyams_content/shared/imagemap/paragraph.py:97
 #, python-format
 msgid "image map '{0}' can't be found"
 msgstr "l'image cliquable '{0}' est introuvable"
 
-#: src/pyams_content/shared/imagemap/paragraph.py:103
+#: src/pyams_content/shared/imagemap/paragraph.py:105
 #, python-format
 msgid "image map '{0}' is not published"
 msgstr "l'image cliquable '{0}' n'est pas publiée"
@@ -4239,19 +4311,19 @@
 msgid "Add automatic content archiver"
 msgstr "Ajout d'une tâche d'archivage automatique"
 
-#: src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:47
+#: src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:46
 msgid "Shared sites"
 msgstr "Sites et blogs"
 
-#: src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:66
+#: src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:67
 msgid "Shared contents"
 msgstr "Gabarits"
 
-#: src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:87
+#: src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:90
 msgid "Shared tools"
 msgstr "Outils"
 
-#: src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:108
+#: src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:113
 msgid "My roles"
 msgstr "Mes rôles"
 
@@ -4364,19 +4436,15 @@
 msgid " - {field}: <span class=\"text-danger\">{message}</span>"
 msgstr " - {field} : <span class=\"text-danger\">{message}</span>"
 
-#: src/pyams_content/features/checker/zmi/__init__.py:43
-msgid "Check content..."
-msgstr "Vérifier le contenu"
-
-#: src/pyams_content/features/checker/zmi/__init__.py:55
+#: src/pyams_content/features/checker/zmi/__init__.py:58
 msgid "Content check"
-msgstr "Vérification de contenu"
-
-#: src/pyams_content/features/checker/zmi/__init__.py:79
+msgstr "Audit du contenu"
+
+#: src/pyams_content/features/checker/zmi/__init__.py:82
 msgid "No checker available. This content is clean!"
 msgstr "Pas de vérificateur disponible. Ce contenu est propre !"
 
-#: src/pyams_content/features/checker/zmi/__init__.py:75
+#: src/pyams_content/features/checker/zmi/__init__.py:78
 #, python-format
 msgid "{0}:"
 msgstr "{0} :"
@@ -4529,17 +4597,17 @@
 msgid "Presentation template used for this footer"
 msgstr "Mode de rendu utilisé par ce pied de page"
 
-#: src/pyams_content/features/review/__init__.py:186
+#: src/pyams_content/features/review/__init__.py:180
 #, python-format
 msgid "Request comment: {comment}"
 msgstr "Commentaire joint : {comment}"
 
-#: src/pyams_content/features/review/__init__.py:216
+#: src/pyams_content/features/review/__init__.py:210
 #, python-format
 msgid "A new comment was added on content « {0} »"
 msgstr "Un nouveau commentaire a été ajouté pour le contenu « {0} »"
 
-#: src/pyams_content/features/review/__init__.py:173
+#: src/pyams_content/features/review/__init__.py:167
 #, python-format
 msgid "[{service_name}] A content review is requested"
 msgstr "[{service_name}] Demande de relecture"
@@ -4745,6 +4813,9 @@
 msgid "Hidden header"
 msgstr "Ne pas afficher d'en-tête de pages"
 
+#~ msgid "Check content..."
+#~ msgstr "Auditer le contenu"
+
 #~ msgid "Simple list view"
 #~ msgstr "Vue simple d'une liste d'éléments"
 
--- a/src/pyams_content/locales/pyams_content.pot	Tue May 29 09:51:54 2018 +0200
+++ b/src/pyams_content/locales/pyams_content.pot	Wed May 30 11:23:14 2018 +0200
@@ -6,7 +6,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-05-24 09:43+0200\n"
+"POT-Creation-Date: 2018-05-30 11:11+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -82,7 +82,7 @@
 msgid "Medias gallery"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/__init__.py:159
+#: ./src/pyams_content/component/gallery/__init__.py:154
 msgid "Gallery"
 msgstr ""
 
@@ -308,8 +308,8 @@
 
 #: ./src/pyams_content/component/extfile/__init__.py:255
 #: ./src/pyams_content/component/extfile/__init__.py:260
-#: ./src/pyams_content/component/paragraph/video.py:49
-#: ./src/pyams_content/component/paragraph/video.py:62
+#: ./src/pyams_content/component/paragraph/video.py:51
+#: ./src/pyams_content/component/paragraph/video.py:64
 msgid "Video"
 msgstr ""
 
@@ -455,7 +455,7 @@
 
 #: ./src/pyams_content/component/illustration/paragraph.py:40
 #: ./src/pyams_content/component/illustration/paragraph.py:47
-#: ./src/pyams_content/component/illustration/__init__.py:134
+#: ./src/pyams_content/component/illustration/__init__.py:132
 #: ./src/pyams_content/component/illustration/zmi/__init__.py:54
 #: ./src/pyams_content/component/illustration/zmi/__init__.py:81
 msgid "Illustration"
@@ -489,13 +489,13 @@
 msgid "Presentation template used for illustration"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/milestone.py:205
-#: ./src/pyams_content/component/paragraph/milestone.py:227
+#: ./src/pyams_content/component/paragraph/milestone.py:199
+#: ./src/pyams_content/component/paragraph/milestone.py:222
 #: ./src/pyams_content/component/paragraph/zmi/milestone.py:297
 msgid "Milestones"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/milestone.py:236
+#: ./src/pyams_content/component/paragraph/milestone.py:231
 msgid "Milestones paragraph"
 msgstr ""
 
@@ -507,12 +507,12 @@
 msgid "Selected paragraph is not visible"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/keypoint.py:44
+#: ./src/pyams_content/component/paragraph/keypoint.py:46
 #: ./src/pyams_content/component/paragraph/interfaces/keypoint.py:39
 msgid "Key points"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/keypoint.py:59
+#: ./src/pyams_content/component/paragraph/keypoint.py:61
 msgid "Key points paragraph"
 msgstr ""
 
@@ -524,13 +524,13 @@
 msgid "no visible paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/pictogram.py:195
-#: ./src/pyams_content/component/paragraph/pictogram.py:217
+#: ./src/pyams_content/component/paragraph/pictogram.py:189
+#: ./src/pyams_content/component/paragraph/pictogram.py:212
 #: ./src/pyams_content/component/paragraph/zmi/pictogram.py:301
 msgid "Pictograms"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/pictogram.py:226
+#: ./src/pyams_content/component/paragraph/pictogram.py:221
 msgid "Pictograms paragraph"
 msgstr ""
 
@@ -538,61 +538,61 @@
 msgid "Selected pictogram is missing"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/keynumber.py:189
-#: ./src/pyams_content/component/paragraph/keynumber.py:212
+#: ./src/pyams_content/component/paragraph/keynumber.py:183
+#: ./src/pyams_content/component/paragraph/keynumber.py:207
 #: ./src/pyams_content/component/paragraph/zmi/keynumber.py:276
 msgid "Key numbers"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/keynumber.py:221
+#: ./src/pyams_content/component/paragraph/keynumber.py:216
 msgid "Key numbers paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/frame.py:50
+#: ./src/pyams_content/component/paragraph/frame.py:52
 msgid "Framed text"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/frame.py:60
+#: ./src/pyams_content/component/paragraph/frame.py:62
 msgid "Framed text paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/verbatim.py:48
+#: ./src/pyams_content/component/paragraph/verbatim.py:50
 msgid "Verbatim"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/verbatim.py:60
+#: ./src/pyams_content/component/paragraph/verbatim.py:62
 msgid "Verbatim paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/html.py:61
+#: ./src/pyams_content/component/paragraph/html.py:63
 msgid "Raw HTML "
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/html.py:71
+#: ./src/pyams_content/component/paragraph/html.py:73
 msgid "Raw HTML paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/html.py:117
+#: ./src/pyams_content/component/paragraph/html.py:119
 msgid "Rich text"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/html.py:127
+#: ./src/pyams_content/component/paragraph/html.py:129
 msgid "Rich text paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/contact.py:45
-#: ./src/pyams_content/component/paragraph/contact.py:74
+#: ./src/pyams_content/component/paragraph/contact.py:47
+#: ./src/pyams_content/component/paragraph/contact.py:76
 msgid "Contact card"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/header.py:44
+#: ./src/pyams_content/component/paragraph/header.py:46
 #: ./src/pyams_content/component/paragraph/interfaces/header.py:39
 #: ./src/pyams_content/features/alert/interfaces.py:65
 #: ./src/pyams_content/features/alert/zmi/container.py:158
 msgid "Header"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/header.py:59
+#: ./src/pyams_content/component/paragraph/header.py:61
 msgid "Header paragraph"
 msgstr ""
 
@@ -663,35 +663,36 @@
 "NOTICE: removing types from allowed types list will have no effect on already created contents!"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:208
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:214
+#: ./src/pyams_content/shared/common/zmi/templates/preview-input.pt:39
 #: ./src/pyams_content/features/preview/zmi/__init__.py:45
 msgid "Preview"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:213
-#: ./src/pyams_content/shared/common/zmi/workflow.py:115
-#: ./src/pyams_content/shared/common/zmi/workflow.py:207
-#: ./src/pyams_content/shared/common/zmi/workflow.py:252
-#: ./src/pyams_content/shared/common/zmi/workflow.py:311
-#: ./src/pyams_content/shared/common/zmi/workflow.py:405
-#: ./src/pyams_content/shared/common/zmi/workflow.py:466
-#: ./src/pyams_content/shared/common/zmi/workflow.py:511
-#: ./src/pyams_content/shared/common/zmi/workflow.py:557
-#: ./src/pyams_content/shared/common/zmi/workflow.py:605
-#: ./src/pyams_content/shared/common/zmi/workflow.py:650
-#: ./src/pyams_content/shared/common/zmi/workflow.py:696
-#: ./src/pyams_content/shared/common/zmi/workflow.py:752
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:219
+#: ./src/pyams_content/shared/common/zmi/workflow.py:125
+#: ./src/pyams_content/shared/common/zmi/workflow.py:217
+#: ./src/pyams_content/shared/common/zmi/workflow.py:262
+#: ./src/pyams_content/shared/common/zmi/workflow.py:321
+#: ./src/pyams_content/shared/common/zmi/workflow.py:415
+#: ./src/pyams_content/shared/common/zmi/workflow.py:476
+#: ./src/pyams_content/shared/common/zmi/workflow.py:521
+#: ./src/pyams_content/shared/common/zmi/workflow.py:567
+#: ./src/pyams_content/shared/common/zmi/workflow.py:615
+#: ./src/pyams_content/shared/common/zmi/workflow.py:660
+#: ./src/pyams_content/shared/common/zmi/workflow.py:706
+#: ./src/pyams_content/shared/common/zmi/workflow.py:762
 #: ./src/pyams_content/shared/common/zmi/__init__.py:276
 #: ./src/pyams_content/shared/common/zmi/owner.py:74
 #: ./src/pyams_content/features/review/zmi/__init__.py:90
 msgid "Cancel"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:215
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:221
 msgid "Submit"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:200
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:202
 msgid "Paragraph was correctly added."
 msgstr ""
 
@@ -1112,7 +1113,7 @@
 msgstr ""
 
 #: ./src/pyams_content/component/paragraph/interfaces/html.py:44
-msgid "raw HTML code template"
+msgid "Raw HTML code template"
 msgstr ""
 
 #: ./src/pyams_content/component/paragraph/interfaces/html.py:64
@@ -1185,13 +1186,13 @@
 msgid "Presentation template used for this header"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/__init__.py:81
+#: ./src/pyams_content/component/theme/__init__.py:65
 #: ./src/pyams_content/component/theme/zmi/portlet.py:40
 #: ./src/pyams_content/component/theme/interfaces/__init__.py:43
 msgid "Themes"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/__init__.py:90
+#: ./src/pyams_content/component/theme/__init__.py:74
 msgid "no defined theme"
 msgstr ""
 
@@ -1212,8 +1213,8 @@
 msgid "Selected themes"
 msgstr ""
 
-#: ./src/pyams_content/component/association/paragraph.py:46
-#: ./src/pyams_content/component/association/paragraph.py:55
+#: ./src/pyams_content/component/association/paragraph.py:48
+#: ./src/pyams_content/component/association/paragraph.py:57
 msgid "Associations paragraph"
 msgstr ""
 
@@ -1384,9 +1385,9 @@
 msgid "Address as displayed in address book"
 msgstr ""
 
-#: ./src/pyams_content/component/video/paragraph.py:45
-#: ./src/pyams_content/component/video/paragraph.py:55
-#: ./src/pyams_content/component/video/__init__.py:80
+#: ./src/pyams_content/component/video/paragraph.py:47
+#: ./src/pyams_content/component/video/paragraph.py:57
+#: ./src/pyams_content/component/video/__init__.py:73
 msgid "External video"
 msgstr ""
 
@@ -1793,50 +1794,54 @@
 msgid "Click to see subtypes"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:116
+#: ./src/pyams_content/shared/common/zmi/workflow.py:907
+msgid "Prior checks"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/workflow.py:126
 msgid "Request publication"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:208
+#: ./src/pyams_content/shared/common/zmi/workflow.py:218
 #: ./src/pyams_content/workflow/__init__.py:315
 msgid "Cancel publication request"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:253
+#: ./src/pyams_content/shared/common/zmi/workflow.py:263
 msgid "Refuse publication request"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:312
+#: ./src/pyams_content/shared/common/zmi/workflow.py:322
 #: ./src/pyams_content/workflow/basic.py:196
 msgid "Publish"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:406
+#: ./src/pyams_content/shared/common/zmi/workflow.py:416
 msgid "Request retire"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:467
+#: ./src/pyams_content/shared/common/zmi/workflow.py:477
 msgid "Cancel retire request"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:512
+#: ./src/pyams_content/shared/common/zmi/workflow.py:522
 msgid "Retire"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:558
+#: ./src/pyams_content/shared/common/zmi/workflow.py:568
 #: ./src/pyams_content/workflow/__init__.py:436
 msgid "Request archive"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:606
+#: ./src/pyams_content/shared/common/zmi/workflow.py:616
 msgid "Cancel archive request"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:651
+#: ./src/pyams_content/shared/common/zmi/workflow.py:661
 msgid "Archive"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:697
+#: ./src/pyams_content/shared/common/zmi/workflow.py:707
 #: ./src/pyams_content/workflow/__init__.py:501
 #: ./src/pyams_content/workflow/__init__.py:513
 #: ./src/pyams_content/workflow/__init__.py:525
@@ -1847,36 +1852,50 @@
 msgid "Create new version"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:753
+#: ./src/pyams_content/shared/common/zmi/workflow.py:763
 #: ./src/pyams_content/workflow/__init__.py:561
 #: ./src/pyams_content/workflow/basic.py:248
 msgid "Delete version"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:178
-#: ./src/pyams_content/shared/common/zmi/workflow.py:375
+#: ./src/pyams_content/shared/common/zmi/workflow.py:853
+msgid "Previewed content?"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/workflow.py:857
+msgid "Verified content?"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/workflow.py:188
+#: ./src/pyams_content/shared/common/zmi/workflow.py:385
 msgid "Publication start date is required"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:281
-#: ./src/pyams_content/shared/common/zmi/workflow.py:437
+#: ./src/pyams_content/shared/common/zmi/workflow.py:291
+#: ./src/pyams_content/shared/common/zmi/workflow.py:447
 msgid "A comment is required"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:776
+#: ./src/pyams_content/shared/common/zmi/workflow.py:786
 msgid "Delete content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:785
+#: ./src/pyams_content/shared/common/zmi/workflow.py:795
 msgid "Delete definitively"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:80
+#: ./src/pyams_content/shared/common/zmi/workflow.py:920
+msgid ""
+"You must confirm that you previewed and checked this content before "
+"requesting publication!!"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/workflow.py:90
 #, python-format
 msgid "{state} | by {principal}"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:76
+#: ./src/pyams_content/shared/common/zmi/workflow.py:86
 #: ./src/pyams_content/workflow/__init__.py:648
 #: ./src/pyams_content/workflow/__init__.py:619
 #: ./src/pyams_content/workflow/basic.py:315
@@ -2305,36 +2324,64 @@
 msgid "Content publication start date is not passed yet"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:61
+#: ./src/pyams_content/shared/common/zmi/security.py:65
+msgid "Contributors restrictions"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/security.py:74
+msgid "Content contributors restrictions"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/security.py:108
+msgid "Contributor name"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/security.py:119
+#: ./src/pyams_content/shared/common/zmi/security.py:277
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:252
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:277
+msgid "Publication checks"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/security.py:225
 msgid "Managers restrictions"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:70
+#: ./src/pyams_content/shared/common/zmi/security.py:234
 msgid "Content managers restrictions"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:102
+#: ./src/pyams_content/shared/common/zmi/security.py:266
 msgid "Manager name"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:112
+#: ./src/pyams_content/shared/common/zmi/security.py:294
 msgid "Restricted"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:129
+#: ./src/pyams_content/shared/common/zmi/security.py:311
 msgid "Owners"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:178
+#: ./src/pyams_content/shared/common/zmi/security.py:401
+msgid "Publication workflow"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/security.py:168
+#, python-format
+msgid "Edit contributor restrictions for « {0} »"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/security.py:360
 #, python-format
 msgid "Edit manager restrictions for « {0} »"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:220
+#: ./src/pyams_content/shared/common/zmi/security.py:407
 msgid "Apply contents restrictions"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:222
+#: ./src/pyams_content/shared/common/zmi/security.py:409
 msgid ""
 "You can specify which contents this manager will be able to manage. If you "
 "specify several criteria, the manager will be able to manage contents for "
@@ -2402,6 +2449,19 @@
 "again except by creating a new version."
 msgstr ""
 
+#: ./src/pyams_content/shared/common/zmi/templates/check-input.pt:34
+#: ./src/pyams_content/shared/common/zmi/templates/preview-input.pt:34
+#: ./src/pyams_content/shared/common/interfaces/types.py:39
+#: ./src/pyams_content/shared/form/zmi/field.py:159
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:61
+msgid "Label"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/templates/check-input.pt:39
+#: ./src/pyams_content/features/checker/zmi/__init__.py:43
+msgid "Audit"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/zmi/templates/dashboard.pt:28
 #: ./src/pyams_content/root/zmi/templates/dashboard.pt:28
 msgid "Quick search..."
@@ -2594,12 +2654,6 @@
 msgid "Name of this data type; must be unique between all data types"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:39
-#: ./src/pyams_content/shared/form/zmi/field.py:159
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:61
-msgid "Label"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/types.py:42
 msgid "Navigation label"
 msgstr ""
@@ -2824,25 +2878,37 @@
 "Guests are users which are allowed to view contents with restricted access"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:216
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:213
 msgid "Principal ID"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:221
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:253
+msgid ""
+"If 'yes', this contributor will have to confirm that contents have been "
+"previewed and checked before asking for publication"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:278
+msgid ""
+"If 'yes', this manager will have to confirm that contents have been previewed"
+" and checked before publishing a content"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:283
 msgid "Restricted contents"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:222
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:284
 msgid ""
 "If 'yes', this manager will get restricted access to manage contents based on"
 " selected settings"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:227
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:289
 msgid "Selected owners"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:228
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:290
 msgid "Manager will have access to contents owned by these principals"
 msgstr ""
 
@@ -2854,51 +2920,51 @@
 msgid "no field defined"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/handler.py:85
+#: ./src/pyams_content/shared/form/handler.py:80
 msgid "Mailto form handler"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/handler.py:45
+#: ./src/pyams_content/shared/form/handler.py:44
 msgid "No selected handler..."
 msgstr ""
 
+#: ./src/pyams_content/shared/form/field.py:137
+msgid "Text"
+msgstr ""
+
 #: ./src/pyams_content/shared/form/field.py:147
-msgid "Text"
+msgid "Multi-lines text"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/field.py:157
-msgid "Multi-lines text"
+msgid "Boolean"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/field.py:167
-msgid "Boolean"
+msgid "Integer"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/field.py:177
-msgid "Integer"
+msgid "Decimal"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/field.py:187
-msgid "Decimal"
+msgid "E-mail address"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/field.py:197
-msgid "E-mail address"
+msgid "URI"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/field.py:207
-msgid "URI"
-msgstr ""
-
-#: ./src/pyams_content/shared/form/field.py:217
 msgid "Date"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/field.py:231
+#: ./src/pyams_content/shared/form/field.py:221
 msgid "Choice"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/field.py:251
+#: ./src/pyams_content/shared/form/field.py:241
 msgid "List"
 msgstr ""
 
@@ -3248,22 +3314,22 @@
 msgid "Other terms"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/paragraph.py:45
-#: ./src/pyams_content/shared/imagemap/paragraph.py:58
+#: ./src/pyams_content/shared/imagemap/paragraph.py:47
+#: ./src/pyams_content/shared/imagemap/paragraph.py:60
 #: ./src/pyams_content/shared/imagemap/interfaces/__init__.py:35
 msgid "Image map"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/paragraph.py:89
+#: ./src/pyams_content/shared/imagemap/paragraph.py:91
 msgid "no selected image map"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/paragraph.py:95
+#: ./src/pyams_content/shared/imagemap/paragraph.py:97
 #, python-format
 msgid "image map '{0}' can't be found"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/paragraph.py:103
+#: ./src/pyams_content/shared/imagemap/paragraph.py:105
 #, python-format
 msgid "image map '{0}' is not published"
 msgstr ""
@@ -4023,19 +4089,19 @@
 msgid "Add automatic content archiver"
 msgstr ""
 
-#: ./src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:47
+#: ./src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:46
 msgid "Shared sites"
 msgstr ""
 
-#: ./src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:66
+#: ./src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:67
 msgid "Shared contents"
 msgstr ""
 
-#: ./src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:87
+#: ./src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:90
 msgid "Shared tools"
 msgstr ""
 
-#: ./src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:108
+#: ./src/pyams_content/skin/zmi/viewlet/toplinks/__init__.py:113
 msgid "My roles"
 msgstr ""
 
@@ -4148,19 +4214,15 @@
 msgid " - {field}: <span class=\"text-danger\">{message}</span>"
 msgstr ""
 
-#: ./src/pyams_content/features/checker/zmi/__init__.py:43
-msgid "Check content..."
-msgstr ""
-
-#: ./src/pyams_content/features/checker/zmi/__init__.py:55
+#: ./src/pyams_content/features/checker/zmi/__init__.py:58
 msgid "Content check"
 msgstr ""
 
-#: ./src/pyams_content/features/checker/zmi/__init__.py:79
+#: ./src/pyams_content/features/checker/zmi/__init__.py:82
 msgid "No checker available. This content is clean!"
 msgstr ""
 
-#: ./src/pyams_content/features/checker/zmi/__init__.py:75
+#: ./src/pyams_content/features/checker/zmi/__init__.py:78
 #, python-format
 msgid "{0}:"
 msgstr ""
@@ -4303,17 +4365,17 @@
 msgid "Presentation template used for this footer"
 msgstr ""
 
-#: ./src/pyams_content/features/review/__init__.py:186
+#: ./src/pyams_content/features/review/__init__.py:180
 #, python-format
 msgid "Request comment: {comment}"
 msgstr ""
 
-#: ./src/pyams_content/features/review/__init__.py:216
+#: ./src/pyams_content/features/review/__init__.py:210
 #, python-format
 msgid "A new comment was added on content « {0} »"
 msgstr ""
 
-#: ./src/pyams_content/features/review/__init__.py:173
+#: ./src/pyams_content/features/review/__init__.py:167
 #, python-format
 msgid "[{service_name}] A content review is requested"
 msgstr ""
--- a/src/pyams_content/shared/common/interfaces/__init__.py	Tue May 29 09:51:54 2018 +0200
+++ b/src/pyams_content/shared/common/interfaces/__init__.py	Wed May 30 11:23:14 2018 +0200
@@ -204,19 +204,81 @@
 
 
 #
+# Generic restrictions interfaces
+#
+
+class IRestrictionInfo(Interface):
+    """User restriction base interface"""
+
+    principal_id = Principal(title=_("Principal ID"),
+                             required=True)
+
+    restriction_interface = Attribute("Restrictions interface")
+
+
+class IRestrictions(Interface):
+    """Restrictions manager interface"""
+
+    restrictions_key = Attribute("Restrictions annotations key")
+    restrictions_factory_interface = Attribute("Restrictions factory")
+
+    def get_restrictions(self, principal, create_if_none=False):
+        """Get manager restrictions for given principal"""
+
+    def new_restrictions(self, principal):
+        """Create new manager restrictions"""
+
+    def set_restrictions(self, principal, restrictions=None):
+        """Set manager restrictions for given principal"""
+
+    def drop_restrictions(self, principal):
+        """Drop manager restrictions for given principal"""
+
+
+class IRestrictionsFactory(Interface):
+    """Restrictions factory interface"""
+
+
+#
+# Shared tool contributor security restrictions
+#
+
+CONTRIBUTOR_RESTRICTIONS_KEY = 'pyams_content.contributor.restrictions'
+
+
+class IContributorRestrictionInfo(IRestrictionInfo):
+    """Shared content contributor restrictions"""
+
+    publication_checks = Bool(title=_("Publication checks"),
+                              description=_("If 'yes', this contributor will have to confirm that contents have "
+                                            "been previewed and checked before asking for publication"),
+                              required=False,
+                              default=True)
+
+
+class IContributorRestrictions(IRestrictions):
+    """Contributor restrictions interface"""
+
+
+class IContributorRestrictionsFactory(IRestrictionsFactory):
+    """Contributor restrictions factory interface"""
+
+
+#
 # Shared tool manager security restrictions
 #
 
 MANAGER_RESTRICTIONS_KEY = 'pyams_content.manager.restrictions'
 
 
-class IManagerRestrictionInfo(Interface):
+class IManagerRestrictionInfo(IRestrictionInfo):
     """Shared content manager restrictions"""
 
-    principal_id = Principal(title=_("Principal ID"),
-                             required=True)
-
-    restriction_interface = Attribute("Restrictions interface")
+    publication_checks = Bool(title=_("Publication checks"),
+                              description=_("If 'yes', this manager will have to confirm that contents have "
+                                            "been previewed and checked before publishing a content"),
+                              required=False,
+                              default=True)
 
     restricted_contents = Bool(title=_("Restricted contents"),
                                description=_("If 'yes', this manager will get restricted access to manage contents "
@@ -232,18 +294,9 @@
         """Check if principal is granted access to given content"""
 
 
-class IManagerRestrictionsFactory(Interface):
-    """Manager restrictions factory interface"""
+class IManagerRestrictions(IRestrictions):
+    """Manager restrictions interface"""
 
 
-class IManagerRestrictions(Interface):
-    """Manager restrictions"""
-
-    def get_restrictions(self, principal):
-        """Get manager restrictions for given principal"""
-
-    def set_restrictions(self, principal, restrictions):
-        """Set manager restrictions for given principal"""
-
-    def drop_restrictions(self, principal):
-        """Drop manager restrictions for given principal"""
+class IManagerRestrictionsFactory(IRestrictionsFactory):
+    """Manager restrictions factory interface"""
--- a/src/pyams_content/shared/common/security.py	Tue May 29 09:51:54 2018 +0200
+++ b/src/pyams_content/shared/common/security.py	Wed May 30 11:23:14 2018 +0200
@@ -16,9 +16,11 @@
 # import standard library
 
 # import interfaces
-from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION, MANAGER_ROLE
+from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION, MANAGER_ROLE, CONTRIBUTOR_ROLE
 from pyams_content.shared.common.interfaces import IWfSharedContent, IManagerRestrictions, MANAGER_RESTRICTIONS_KEY, \
-    IManagerRestrictionsFactory, IBaseSharedTool, IManagerRestrictionInfo
+    IManagerRestrictionsFactory, IBaseSharedTool, IManagerRestrictionInfo, IRestrictionInfo, \
+    IContributorRestrictionInfo, IContributorRestrictions, IContributorRestrictionsFactory, \
+    CONTRIBUTOR_RESTRICTIONS_KEY, IRestrictions
 
 # import packages
 from persistent import Persistent
@@ -32,19 +34,123 @@
 from zope.schema.fieldproperty import FieldProperty
 
 
+@implementer(IRestrictionInfo)
+class PrincipalRestrictionInfo(Persistent):
+    """Principal restriction info"""
+
+    principal_id = FieldProperty(IManagerRestrictionInfo['principal_id'])
+
+    def __init__(self, principal_id):
+        self.principal_id = principal_id
+
+
+@implementer(IRestrictions)
+class PrincipalRestrictions(ContextAdapter):
+    """Shared tool restrictions"""
+
+    restrictions_key = None
+    restrictions_factory_interface = None
+
+    def new_restrictions(self, principal):
+        if IPrincipalInfo.providedBy(principal):
+            principal = principal.id
+        factory = self.restrictions_factory_interface(self.context)
+        return factory(principal)
+
+    def get_restrictions(self, principal, create_if_none=False):
+        if IPrincipalInfo.providedBy(principal):
+            principal = principal.id
+        restrictions_folder = get_annotation_adapter(self.context, self.restrictions_key, Folder)
+        restrictions = restrictions_folder.get(principal)
+        if (restrictions is None) and create_if_none:
+            restrictions = self.new_restrictions(principal)
+            self.set_restrictions(principal, restrictions)
+        return restrictions
+
+    def set_restrictions(self, principal, restrictions=None):
+        if IPrincipalInfo.providedBy(principal):
+            principal = principal.id
+        restrictions_folder = get_annotation_adapter(self.context, self.restrictions_key, Folder)
+        if restrictions is None:
+            restrictions = self.new_restrictions(principal)
+        restrictions_folder[principal] = restrictions
+
+    def drop_restrictions(self, principal):
+        restrictions_folder = get_annotation_adapter(self.context, self.restrictions_key)
+        if restrictions_folder is None:
+            return
+        if IPrincipalInfo.providedBy(principal):
+            principal = principal.id
+        if principal in restrictions_folder:
+            del restrictions_folder[principal]
+
+
+#
+# Contributor restrictions
+#
+
+@implementer(IContributorRestrictionInfo)
+class SharedToolContributorRestrictionInfo(PrincipalRestrictionInfo):
+    """Shared tool contributor restriction info"""
+
+    restriction_interface = IContributorRestrictionInfo
+
+    publication_checks = FieldProperty(IContributorRestrictionInfo['publication_checks'])
+
+
+@adapter_config(context=IBaseSharedTool, provides=IContributorRestrictions)
+class SharedToolContributorRestrictions(PrincipalRestrictions):
+    """Shared tool contributor restrictions"""
+
+    restrictions_key = CONTRIBUTOR_RESTRICTIONS_KEY
+    restrictions_factory_interface = IContributorRestrictionsFactory
+
+
+@subscriber(IGrantedRoleEvent, role_selector=CONTRIBUTOR_ROLE)
+def handle_added_contributor_role(event):
+    """Handle added contributor role"""
+    shared_tool = event.object.__parent__
+    contributor_restrictions = IContributorRestrictions(shared_tool, None)
+    if contributor_restrictions:
+        contributor_restrictions.set_restrictions(event.principal_id)
+
+
+@subscriber(IRevokedRoleEvent, role_selector=CONTRIBUTOR_ROLE)
+def handle_revoked_contributor_role(event):
+    """Handle revoked contributor role"""
+    restrictions = IContributorRestrictions(event.object.__parent__, None)
+    if restrictions:
+        restrictions.drop_restrictions(event.principal_id)
+
+
+@adapter_config(context=IWfSharedContent, provides=IContributorRestrictions)
+def shared_content_contributor_restrictions(context):
+    """Shared tool contributor restrictions"""
+    tool = get_parent(context, IBaseSharedTool)
+    if tool is not None:
+        return IContributorRestrictions(tool)
+
+
+@adapter_config(context=IBaseSharedTool, provides=IContributorRestrictionsFactory)
+def shared_tool_contributor_restrictions_factory(context):
+    """Default shared tool contributor restrictions factory"""
+    return SharedToolContributorRestrictionInfo
+
+
+#
+# Manager restrictions
+#
+
 @implementer(IManagerRestrictionInfo)
-class SharedToolManagerRestrictionInfo(Persistent):
+class SharedToolManagerRestrictionInfo(PrincipalRestrictionInfo):
     """Shared tool manager restriction info"""
 
     restriction_interface = IManagerRestrictionInfo
 
-    principal_id = FieldProperty(IManagerRestrictionInfo['principal_id'])
+    publication_checks = FieldProperty(IManagerRestrictionInfo['publication_checks'])
     restricted_contents = FieldProperty(IManagerRestrictionInfo['restricted_contents'])
     owners = FieldProperty(IManagerRestrictionInfo['owners'])
 
-    def __init__(self, principal_id):
-        self.principal_id = principal_id
-
     def check_access(self, context, permission=MANAGE_CONTENT_PERMISSION, request=None):
         if request is None:
             request = check_request()
@@ -58,29 +164,11 @@
 
 
 @adapter_config(context=IBaseSharedTool, provides=IManagerRestrictions)
-class SharedToolManagerRestrictions(ContextAdapter):
+class SharedToolManagerRestrictions(PrincipalRestrictions):
     """Shared tool manager restrictions"""
 
-    def get_restrictions(self, principal):
-        restrictions_folder = get_annotation_adapter(self.context, MANAGER_RESTRICTIONS_KEY, Folder)
-        if IPrincipalInfo.providedBy(principal):
-            principal = principal.id
-        return restrictions_folder.get(principal)
-
-    def set_restrictions(self, principal, restrictions):
-        restrictions_folder = get_annotation_adapter(self.context, MANAGER_RESTRICTIONS_KEY, Folder)
-        if IPrincipalInfo.providedBy(principal):
-            principal = principal.id
-        restrictions_folder[principal] = restrictions
-
-    def drop_restrictions(self, principal):
-        restrictions_folder = get_annotation_adapter(self.context, MANAGER_RESTRICTIONS_KEY)
-        if restrictions_folder is None:
-            return
-        if IPrincipalInfo.providedBy(principal):
-            principal = principal.id
-        if principal in restrictions_folder:
-            del restrictions_folder[principal]
+    restrictions_key = MANAGER_RESTRICTIONS_KEY
+    restrictions_factory_interface = IManagerRestrictionsFactory
 
 
 @subscriber(IGrantedRoleEvent, role_selector=MANAGER_ROLE)
@@ -89,8 +177,7 @@
     shared_tool = event.object.__parent__
     manager_restrictions = IManagerRestrictions(shared_tool, None)
     if manager_restrictions:
-        restrictions = IManagerRestrictionsFactory(shared_tool)(event.principal_id)
-        manager_restrictions.set_restrictions(event.principal_id, restrictions)
+        manager_restrictions.set_restrictions(event.principal_id)
 
 
 @subscriber(IRevokedRoleEvent, role_selector=MANAGER_ROLE)
--- a/src/pyams_content/shared/common/zmi/security.py	Tue May 29 09:51:54 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/security.py	Wed May 30 11:23:14 2018 +0200
@@ -17,7 +17,7 @@
 
 # import interfaces
 from pyams_content.interfaces import MANAGE_TOOL_PERMISSION
-from pyams_content.shared.common.interfaces import IBaseSharedTool, IManagerRestrictions, IManagerRestrictionsFactory
+from pyams_content.shared.common.interfaces import IBaseSharedTool, IManagerRestrictions, IContributorRestrictions
 from pyams_security.interfaces import ISecurityManager, IPrincipalInfo
 from pyams_security.zmi.interfaces import IObjectSecurityMenu
 from pyams_skin.interfaces import IInnerPage, IPageHeader
@@ -53,8 +53,172 @@
 from pyams_content import _
 
 
+#
+# Contributor restrictions views
+#
+
+@viewlet_config(name='contributors-restrictions.menu', context=IBaseSharedTool, layer=IAdminLayer,
+                manager=IObjectSecurityMenu, permission=MANAGE_TOOL_PERMISSION, weight=910)
+class SharedToolContributorsRestrictionsMenu(MenuItem):
+    """Shared tool contributors restrictions menu"""
+
+    label = _("Contributors restrictions")
+    icon_class = 'fa-lock'
+    url = '#contributors-restrictions.html'
+
+
+class SharedToolContributorsRestrictionsTable(BaseTable):
+    """Shared tool contributors restrictions table"""
+
+    id = 'security_contributors_restrictions'
+    title = _("Content contributors restrictions")
+
+
+@adapter_config(context=(IBaseSharedTool, IAdminLayer, SharedToolContributorsRestrictionsTable), provides=IValues)
+class SharedToolContributorsRestrictionsValuesAdapter(ContextRequestViewAdapter):
+    """Shared tool contributor restrictions values adapter"""
+
+    @property
+    def values(self):
+        contributor = get_utility(ISecurityManager)
+        return sorted([contributor.get_principal(principal_id) for principal_id in self.context.contributors],
+                      key=lambda x: x.title)
+
+
+@adapter_config(context=(IPrincipalInfo, IAdminLayer, SharedToolContributorsRestrictionsTable),
+                provides=ITableElementEditor)
+class ContributorInfoElementEditor(ContextRequestViewAdapter):
+    """Contributor info element editor"""
+
+    view_name = 'contributor-restrictions.html'
+
+    @property
+    def url(self):
+        return resource_url(self.view.context, self.request, self.view_name, query={'principal_id': self.context.id})
+
+    modal_target = True
+
+
+@adapter_config(name='name',
+                context=(IBaseSharedTool, IAdminLayer, SharedToolContributorsRestrictionsTable),
+                provides=IColumn)
+class SharedToolContributorsRestrictionsNameColumn(I18nColumn, GetAttrColumn):
+    """Shared tool contributor restrictions name column"""
+
+    _header = _("Contributor name")
+    attrName = 'title'
+    weight = 10
+
+
+@adapter_config(name='checks',
+                context=(IBaseSharedTool, IAdminLayer, SharedToolContributorsRestrictionsTable),
+                provides=IColumn)
+class SharedToolContributorsRestrictionsChecksColumn(I18nColumn, GetAttrColumn):
+    """Shared tool contributor enabled publication checks column"""
+
+    _header = _("Publication checks")
+    weight = 20
+    cssClasses = {'td': 'center'}
+
+    def getValue(self, obj):
+        restrictions = IContributorRestrictions(self.context).get_restrictions(obj, create_if_none=True)
+        if restrictions.publication_checks:
+            return '<i class="fa fa-fw fa-check"></i>'
+        else:
+            return '--'
+
+
+@pagelet_config(name='contributors-restrictions.html', context=IBaseSharedTool, layer=IPyAMSLayer,
+                permission=MANAGE_TOOL_PERMISSION)
+@implementer(IInnerPage)
+class SharedToolContributorsRestrictionsView(AdminView, ContainerView):
+    """Shared tool contributors restrictions view"""
+
+    table_class = SharedToolContributorsRestrictionsTable
+
+
+@adapter_config(context=(IBaseSharedTool, IAdminLayer, SharedToolContributorsRestrictionsView), provides=IPageHeader)
+class SharedToolContributorsRestrictionsHeaderAdapter(DefaultPageHeaderAdapter):
+    """Shared tool contributors restrictions header adapter"""
+
+    back_url = 'admin#protected-object-roles.html'
+    back_target = None
+
+    icon_class = 'fa fa-fw fa-lock'
+
+
+#
+# Contributor restrictions edit form
+#
+
+@pagelet_config(name='contributor-restrictions.html', context=IBaseSharedTool, layer=IPyAMSLayer,
+                permission=MANAGE_TOOL_PERMISSION)
+class SharedToolContributorRestrictionsEditForm(AdminDialogEditForm):
+    """Shared tool contributor restrictions edit form"""
+
+    prefix = 'tool_restrictions.'
+
+    icon_css_class = 'fa fa-fw fa-lock'
+
+    ajax_handler = 'contributor-restrictions.json'
+    edit_permission = MANAGE_TOOL_PERMISSION
+
+    @property
+    def legend(self):
+        return self.request.localizer.translate(_("Edit contributor restrictions for « {0} »")).format(self.principal.title)
+
+    @cached_property
+    def interface(self):
+        restrictions = self.getContent()
+        return restrictions.restriction_interface
+
+    @property
+    def fields(self):
+        fields = field.Fields(self.interface)
+        fields['publication_checks'].widgetFactory = SingleCheckBoxFieldWidget
+        return fields
+
+    @cached_property
+    def principal_id(self):
+        principal_id = self.request.params.get('principal_id') or \
+                       self.request.params.get('{0}widgets.principal_id'.format(self.prefix))
+        if not principal_id:
+            raise NotFound
+        return principal_id
+
+    @cached_property
+    def principal(self):
+        manager = get_utility(ISecurityManager)
+        return manager.get_principal(self.principal_id)
+
+    def getContent(self):
+        contributor_restrictions = IContributorRestrictions(self.context)
+        return contributor_restrictions.get_restrictions(self.principal_id, create_if_none=True)
+
+    def updateWidgets(self, prefix=None):
+        super(SharedToolContributorRestrictionsEditForm, self).updateWidgets(prefix)
+        self.widgets['principal_id'].value = self.principal
+        self.widgets['principal_id'].mode = HIDDEN_MODE
+
+
+@view_config(name='contributor-restrictions.json', context=IBaseSharedTool, request_type=IPyAMSLayer,
+             permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True)
+class SharedToolContributorRestrictionsAJAXEditForm(AJAXEditForm, SharedToolContributorRestrictionsEditForm):
+    """Shared tool contributor restrictions edit form, JSON renderer"""
+
+    def get_ajax_output(self, changes):
+        if changes:
+            return {'status': 'reload'}
+        else:
+            return super(SharedToolContributorRestrictionsAJAXEditForm, self).get_ajax_output(changes)
+
+
+#
+# Manager restrictions views
+#
+
 @viewlet_config(name='managers-restrictions.menu', context=IBaseSharedTool, layer=IAdminLayer,
-                manager=IObjectSecurityMenu, permission=MANAGE_TOOL_PERMISSION, weight=910)
+                manager=IObjectSecurityMenu, permission=MANAGE_TOOL_PERMISSION, weight=920)
 class SharedToolManagersRestrictionsMenu(MenuItem):
     """Shared tool managers restrictions menu"""
 
@@ -66,12 +230,12 @@
 class SharedToolManagersRestrictionsTable(BaseTable):
     """Shared tool manager restrictions table"""
 
-    id = 'security_manager_restrictions'
+    id = 'security_managers_restrictions'
     title = _("Content managers restrictions")
 
 
 @adapter_config(context=(IBaseSharedTool, IAdminLayer, SharedToolManagersRestrictionsTable), provides=IValues)
-class SharedToolManagerRestrictionsValuesAdapter(ContextRequestViewAdapter):
+class SharedToolManagersRestrictionsValuesAdapter(ContextRequestViewAdapter):
     """Shared tool manager restrictions values adapter"""
 
     @property
@@ -83,7 +247,7 @@
 
 @adapter_config(context=(IPrincipalInfo, IAdminLayer, SharedToolManagersRestrictionsTable),
                 provides=ITableElementEditor)
-class PrincipalInfoElementEditor(ContextRequestViewAdapter):
+class ManagerInfoElementEditor(ContextRequestViewAdapter):
     """Principal info element editor"""
 
     view_name = 'manager-restrictions.html'
@@ -104,6 +268,24 @@
     weight = 10
 
 
+@adapter_config(name='checks',
+                context=(IBaseSharedTool, IAdminLayer, SharedToolManagersRestrictionsTable),
+                provides=IColumn)
+class SharedToolManagerRestrictionsChecksColumn(I18nColumn, GetAttrColumn):
+    """Shared tool manager enabled publication checks column"""
+
+    _header = _("Publication checks")
+    weight = 40
+    cssClasses = {'td': 'center'}
+
+    def getValue(self, obj):
+        restrictions = IManagerRestrictions(self.context).get_restrictions(obj, create_if_none=True)
+        if restrictions.publication_checks:
+            return '<i class="fa fa-fw fa-check"></i>'
+        else:
+            return '--'
+
+
 @adapter_config(name='restricted', context=(IBaseSharedTool, IAdminLayer, SharedToolManagersRestrictionsTable),
                 provides=IColumn)
 class SharedToolManagerRestrictionsEnabledColumn(I18nColumn, GetAttrColumn):
@@ -114,8 +296,8 @@
     cssClasses = {'td': 'center'}
 
     def getValue(self, obj):
-        restrictions = IManagerRestrictions(self.context).get_restrictions(obj)
-        if (restrictions is not None) and restrictions.restricted_contents:
+        restrictions = IManagerRestrictions(self.context).get_restrictions(obj, create_if_none=True)
+        if restrictions.restricted_contents:
             return '<i class="fa fa-fw fa-check"></i>'
         else:
             return '--'
@@ -185,6 +367,7 @@
     @property
     def fields(self):
         fields = field.Fields(self.interface)
+        fields['publication_checks'].widgetFactory = SingleCheckBoxFieldWidget
         fields['restricted_contents'].widgetFactory = SingleCheckBoxFieldWidget
         return fields
 
@@ -203,11 +386,7 @@
 
     def getContent(self):
         manager_restrictions = IManagerRestrictions(self.context)
-        restrictions = manager_restrictions.get_restrictions(self.principal_id)
-        if restrictions is None:
-            restrictions = IManagerRestrictionsFactory(self.context)(self.principal_id)
-            manager_restrictions.set_restrictions(self.principal_id, restrictions)
-        return restrictions
+        return manager_restrictions.get_restrictions(self.principal_id, create_if_none=True)
 
     def updateWidgets(self, prefix=None):
         super(SharedToolManagerRestrictionsEditForm, self).updateWidgets(prefix)
@@ -216,6 +395,14 @@
 
     def updateGroups(self):
         names = getFieldNamesInOrder(self.interface)
+        names.remove('publication_checks')
+        group = NamedWidgetsGroup(self, 'publication_checks', self.widgets,
+                                  ('publication_checks',),
+                                  legend=_("Publication workflow"),
+                                  css_class='inner')
+        group.label_css_class = 'control-label col-md-2'
+        group.input_css_class = 'col-md-10'
+        self.add_group(group)
         self.add_group(NamedWidgetsGroup(self, 'restricted_access', self.widgets, names,
                                          legend=_("Apply contents restrictions"),
                                          css_class='inner',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/zmi/templates/check-input.pt	Wed May 30 11:23:14 2018 +0200
@@ -0,0 +1,41 @@
+<div class="inline-group" i18n:domain="pyams_form">
+	<label class="radio"
+		   tal:repeat="item view/items">
+		<input type="radio"
+			   tal:define="checked item/checked"
+			   tal:attributes="id item/id;
+								name item/name;
+								class view/klass;
+								value item/value;
+								style view/style;
+								title view/title;
+								lang view/lang;
+								onclick view/onclick;
+								ondblclick view/ondblclick;
+								onmousedown view/onmousedown;
+								onmouseup view/onmouseup;
+								onmouseover view/onmouseover;
+								onmousemove view/onmousemove;
+								onmouseout view/onmouseout;
+								onkeypress view/onkeypress;
+								onkeydown view/onkeydown;
+								onkeyup view/onkeyup;
+								disabled view/disabled;
+								tabindex view/tabindex;
+								onfocus view/onfocus;
+								onblur view/onblur;
+								onchange view/onchange;
+								readonly view/readonly;
+								alt view/alt;
+								accesskey view/accesskey;
+								onselect view/onselect;
+								checked python: checked and 'checked' or None;
+								data-ams-data extension:object_data(view);" />
+		<i></i><span tal:replace="item/label" i18n:translate="">Label</span>
+	</label>
+	<button class="btn btn-xs col-md-2" i18n:domain="pyams_content"
+			data-ams-url="content-check.html" data-toggle="modal">
+		<i class="fa fa-fw fa-check-square-o"></i>
+		<span i18n:translate="">Audit</span>
+	</button>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/zmi/templates/preview-input.pt	Wed May 30 11:23:14 2018 +0200
@@ -0,0 +1,41 @@
+<div class="inline-group" i18n:domain="pyams_form">
+	<label class="radio"
+		   tal:repeat="item view/items">
+		<input type="radio"
+			   tal:define="checked item/checked"
+			   tal:attributes="id item/id;
+								name item/name;
+								class view/klass;
+								value item/value;
+								style view/style;
+								title view/title;
+								lang view/lang;
+								onclick view/onclick;
+								ondblclick view/ondblclick;
+								onmousedown view/onmousedown;
+								onmouseup view/onmouseup;
+								onmouseover view/onmouseover;
+								onmousemove view/onmousemove;
+								onmouseout view/onmouseout;
+								onkeypress view/onkeypress;
+								onkeydown view/onkeydown;
+								onkeyup view/onkeyup;
+								disabled view/disabled;
+								tabindex view/tabindex;
+								onfocus view/onfocus;
+								onblur view/onblur;
+								onchange view/onchange;
+								readonly view/readonly;
+								alt view/alt;
+								accesskey view/accesskey;
+								onselect view/onselect;
+								checked python: checked and 'checked' or None;
+								data-ams-data extension:object_data(view);" />
+		<i></i><span tal:replace="item/label" i18n:translate="">Label</span>
+	</label>
+	<button class="btn btn-xs col-md-2" i18n:domain="pyams_content"
+			data-ams-url="content-preview.html" data-toggle="modal">
+		<i class="fa fa-fw fa-binoculars"></i>
+		<span i18n:translate="">Preview</span>
+	</button>
+</div>
--- a/src/pyams_content/shared/common/zmi/workflow.py	Tue May 29 09:51:54 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/workflow.py	Wed May 30 11:23:14 2018 +0200
@@ -9,6 +9,12 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from z3c.form.browser.radio import RadioWidget
+from z3c.form.widget import FieldWidget
+
+from pyams_form.group import NamedWidgetsGroup
+from pyams_form.interfaces import IFormLayer
+from pyams_form.widget import widgettemplate_config
 
 __docformat__ = 'restructuredtext'
 
@@ -18,8 +24,9 @@
 
 # import interfaces
 from pyams_content.interfaces import PUBLISH_CONTENT_PERMISSION, CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION
-from pyams_content.shared.common.interfaces import IWfSharedContent, IBaseSharedTool, ISharedContent
-from pyams_form.interfaces.form import IWidgetsPrefixViewletsManager, IFormSuffixViewletsManager
+from pyams_content.shared.common.interfaces import IWfSharedContent, IBaseSharedTool, ISharedContent, \
+    IContributorRestrictions, IManagerRestrictions
+from pyams_form.interfaces.form import IWidgetsPrefixViewletsManager, IFormSuffixViewletsManager, IInnerSubForm
 from pyams_security.interfaces import ISecurityManager
 from pyams_skin.layer import IPyAMSLayer
 from pyams_workflow.interfaces import IWorkflowInfo, IWorkflowTransitionInfo, IWorkflowPublicationInfo, \
@@ -33,6 +40,7 @@
 from pyams_form.schema import CloseButton
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config
 from pyams_utils.date import format_datetime
 from pyams_utils.registry import get_utility
 from pyams_utils.text import text_to_html
@@ -41,10 +49,12 @@
 from pyams_utils.url import absolute_url
 from pyams_viewlet.viewlet import viewlet_config, Viewlet
 from pyams_workflow.zmi.transition import WorkflowContentTransitionForm, WorkflowContentTransitionAJAXForm
+from pyams_zmi.form import InnerAdminAddForm
 from pyramid.events import subscriber
 from pyramid.view import view_config
 from z3c.form import field, button
 from zope.interface import Interface, Invalid
+from zope.schema import Bool
 
 from pyams_content import _
 
@@ -831,3 +841,84 @@
         if request.principal.id in context.owner:
             return None
         return Viewlet.__new__(cls)
+
+
+#
+# Contributor checks before publication request
+#
+
+class IContributorChecks(Interface):
+    """Contributor checks interface"""
+
+    preview = Bool(title=_("Previewed content?"),
+                   required=True,
+                   default=False)
+
+    check = Bool(title=_("Verified content?"),
+                 required=True,
+                 default=False)
+
+
+@widgettemplate_config(mode='input', template='templates/preview-input.pt', layer=IFormLayer)
+class PreviewWidget(RadioWidget):
+    """Preview check widget"""
+
+
+def PreviewFieldWidget(field, request):
+    return FieldWidget(field, PreviewWidget(request))
+
+
+@widgettemplate_config(mode='input', template='templates/check-input.pt', layer=IFormLayer)
+class CheckWidget(RadioWidget):
+    """Control check widget"""
+
+
+def CheckFieldWidget(field, request):
+    return FieldWidget(field, CheckWidget(request))
+
+
+@adapter_config(name='contributor-checks',
+                context=(IWfSharedContent, IPyAMSLayer, PublicationRequestForm),
+                provides=IInnerSubForm)
+@adapter_config(name='contributor-checks',
+                context=(IWfSharedContent, IPyAMSLayer, PublicationForm),
+                provides=IInnerSubForm)
+class ContributorChecksForm(InnerAdminAddForm):
+    """Contributor checks form"""
+
+    def __new__(cls, context, request, form):
+        principal_id = request.principal.id
+        restrictions = IManagerRestrictions(context, None)
+        if restrictions is not None:
+            manager_restrictions = restrictions.get_restrictions(principal_id)
+            if (manager_restrictions is not None) and \
+               (manager_restrictions.check_access(context, request=request)) and \
+               (not manager_restrictions.publication_checks):
+                return None
+        if isinstance(form, PublicationForm):
+            restrictions = IContributorRestrictions(context, None)
+            if restrictions is not None:
+                contributor_restrictions = restrictions.get_restrictions(principal_id)
+                if (contributor_restrictions is not None) and \
+                   (not contributor_restrictions.publication_checks):
+                    return None
+        return InnerAdminAddForm.__new__(cls)
+
+    legend = _("Prior checks")
+    legend_class = 'inner bold text-danger'
+
+    label_css_class = 'control-label col-md-4'
+    input_css_class = 'col-md-8'
+
+    fields = field.Fields(IContributorChecks)
+    fields['preview'].widgetFactory = PreviewFieldWidget
+    fields['check'].widgetFactory = CheckFieldWidget
+
+
+@subscriber(IDataExtractedEvent, form_selector=ContributorChecksForm)
+def handle_contributor_checks_data_extraction(event):
+    """Handle extraction of contributor checks"""
+    data = event.data
+    if not (data.get('preview') and data.get('check')):
+        event.form.widgets.errors += (Invalid(_("You must confirm that you previewed and checked this content before " 
+                                                "requesting publication!!")), )