Added inner editor for text files
authorThierry Florac <thierry.florac@onf.fr>
Thu, 20 Dec 2018 18:46:31 +0100
changeset 163 bac63d15b259
parent 162 ed538dc075cd
child 164 69b4c4fb6cac
Added inner editor for text files
src/pyams_file/locales/fr/LC_MESSAGES/pyams_file.mo
src/pyams_file/locales/fr/LC_MESSAGES/pyams_file.po
src/pyams_file/locales/pyams_file.pot
src/pyams_file/zmi/file.py
Binary file src/pyams_file/locales/fr/LC_MESSAGES/pyams_file.mo has changed
--- a/src/pyams_file/locales/fr/LC_MESSAGES/pyams_file.po	Thu Dec 20 09:59:19 2018 +0100
+++ b/src/pyams_file/locales/fr/LC_MESSAGES/pyams_file.po	Thu Dec 20 18:46:31 2018 +0100
@@ -5,7 +5,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-11-28 17:19+0100\n"
+"POT-Creation-Date: 2018-12-20 18:42+0100\n"
 "PO-Revision-Date: 2015-02-06 21:39+0100\n"
 "Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
 "Language-Team: French\n"
@@ -16,82 +16,108 @@
 "Generated-By: Lingua 3.8\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-#: src/pyams_file/image.py:51 src/pyams_file/image.py:52
+#: src/pyams_file/image.py:49 src/pyams_file/image.py:50
 msgid "Default thumbnail"
 msgstr "Vignette par défaut"
 
-#: src/pyams_file/image.py:100
+#: src/pyams_file/image.py:98
 msgid "Custom selections"
 msgstr "Sélections spécifiques"
 
-#: src/pyams_file/image.py:176
+#: src/pyams_file/image.py:174
 msgid "Portrait thumbnail"
 msgstr "Vignette portrait"
 
-#: src/pyams_file/image.py:186
+#: src/pyams_file/image.py:184
 msgid "Square thumbnail"
 msgstr "Vignette carrée"
 
-#: src/pyams_file/image.py:196
+#: src/pyams_file/image.py:194
 msgid "Panoramic thumbnail"
 msgstr "Vignette panoramique"
 
-#: src/pyams_file/image.py:213
+#: src/pyams_file/image.py:211
 msgid "Banner thumbnail"
 msgstr "Bandeau"
 
-#: src/pyams_file/image.py:222
+#: src/pyams_file/image.py:220
 msgid "Responsive selections"
 msgstr "Sélections responsives"
 
-#: src/pyams_file/image.py:229
+#: src/pyams_file/image.py:227
 msgid "Smartphone thumbnail"
 msgstr "Smartphone"
 
-#: src/pyams_file/image.py:237
+#: src/pyams_file/image.py:235
 msgid "Tablet thumbnail"
 msgstr "Tablette"
 
-#: src/pyams_file/image.py:245
+#: src/pyams_file/image.py:243
 msgid "Medium screen thumbnail"
 msgstr "Terminaux moyens"
 
-#: src/pyams_file/image.py:253
+#: src/pyams_file/image.py:251
 msgid "Large screen thumbnail"
 msgstr "Grands terminaux"
 
-#: src/pyams_file/interfaces/__init__.py:110
-msgid "Title"
-msgstr "Titre"
+#: src/pyams_file/widget/templates/media-input.pt:7
+#: src/pyams_file/widget/templates/file-input.pt:7
+msgid "Browse..."
+msgstr "Parcourir..."
+
+#: src/pyams_file/widget/templates/media-input.pt:8
+#: src/pyams_file/widget/templates/file-input.pt:8
+msgid "Please select a file..."
+msgstr "Veuillez sélectionner un fichier..."
+
+#: src/pyams_file/widget/templates/media-input.pt:19
+#: src/pyams_file/widget/templates/file-input.pt:19
+msgid "Delete content"
+msgstr "Supprimer ce contenu"
 
-#: src/pyams_file/interfaces/__init__.py:113
-msgid "Description"
-msgstr "Description"
+#: src/pyams_file/widget/templates/media-input.pt:27
+#: src/pyams_file/widget/templates/media-input.pt:62
+#: src/pyams_file/widget/templates/media-input.pt:93
+#: src/pyams_file/widget/templates/file-input.pt:31
+#: src/pyams_file/widget/templates/file-display.pt:14
+#: src/pyams_file/widget/templates/media-display.pt:10
+#: src/pyams_file/widget/templates/media-display.pt:38
+#: src/pyams_file/widget/templates/media-display.pt:62
+msgid "Current value:"
+msgstr "Contenu actuel :"
 
-#: src/pyams_file/interfaces/__init__.py:116
-msgid "Save file as..."
+#: src/pyams_file/widget/templates/media-input.pt:42
+#: src/pyams_file/widget/templates/media-input.pt:79
+#: src/pyams_file/widget/templates/media-input.pt:108
+#: src/pyams_file/widget/templates/file-input.pt:45
+#: src/pyams_file/widget/templates/file-display.pt:21
+#: src/pyams_file/widget/templates/media-display.pt:19
+#: src/pyams_file/widget/templates/media-display.pt:49
+#: src/pyams_file/widget/templates/media-display.pt:70
+msgid "Download"
 msgstr "Enregistrer sous..."
 
-#: src/pyams_file/interfaces/__init__.py:117
-msgid "Name under which the file will be saved"
-msgstr "Nom proposé automatiquement lors de l'enregistrement du fichier"
-
-#: src/pyams_file/interfaces/__init__.py:120
-msgid "Language"
-msgstr "Langue"
-
-#: src/pyams_file/interfaces/__init__.py:121
-msgid "File's content language"
-msgstr "Langue du contenu du fichier"
+#: src/pyams_file/widget/templates/media-input.pt:54
+#: src/pyams_file/widget/templates/media-display.pt:30
+msgid "Zoom image"
+msgstr "Agrandir l'image"
 
 #: src/pyams_file/zmi/file.py:42
 msgid "Properties..."
 msgstr "Propriétés"
 
-#: src/pyams_file/zmi/file.py:56
+#: src/pyams_file/zmi/file.py:57
 msgid "Update file properties"
 msgstr "Mise à jour des propriétés"
 
+#: src/pyams_file/zmi/file.py:92
+msgid "Edit file..."
+msgstr "Éditer le contenu"
+
+#: src/pyams_file/zmi/file.py:108
+msgid "Edit file content"
+msgstr "Editer le contenu du fichier"
+
 #: src/pyams_file/zmi/image.py:120
 msgid "Rotate image to right..."
 msgstr "Tourner l'image vers la droite"
@@ -325,47 +351,29 @@
 "L'image sera redimensionnée (sans jamais être agrandie !) pour être aussi "
 "grande que possible en fonction des contraintes indiquées."
 
-#: src/pyams_file/widget/templates/media-input.pt:7
-#: src/pyams_file/widget/templates/file-input.pt:7
-msgid "Browse..."
-msgstr "Parcourir..."
-
-#: src/pyams_file/widget/templates/media-input.pt:8
-#: src/pyams_file/widget/templates/file-input.pt:8
-msgid "Please select a file..."
-msgstr "Veuillez sélectionner un fichier..."
-
-#: src/pyams_file/widget/templates/media-input.pt:19
-#: src/pyams_file/widget/templates/file-input.pt:19
-msgid "Delete content"
-msgstr "Supprimer ce contenu"
+#: src/pyams_file/interfaces/__init__.py:110
+msgid "Title"
+msgstr "Titre"
 
-#: src/pyams_file/widget/templates/media-input.pt:27
-#: src/pyams_file/widget/templates/media-input.pt:62
-#: src/pyams_file/widget/templates/media-input.pt:93
-#: src/pyams_file/widget/templates/media-display.pt:10
-#: src/pyams_file/widget/templates/media-display.pt:38
-#: src/pyams_file/widget/templates/media-display.pt:62
-#: src/pyams_file/widget/templates/file-input.pt:31
-#: src/pyams_file/widget/templates/file-display.pt:14
-msgid "Current value:"
-msgstr "Contenu actuel :"
+#: src/pyams_file/interfaces/__init__.py:113
+msgid "Description"
+msgstr "Description"
 
-#: src/pyams_file/widget/templates/media-input.pt:42
-#: src/pyams_file/widget/templates/media-input.pt:79
-#: src/pyams_file/widget/templates/media-input.pt:108
-#: src/pyams_file/widget/templates/media-display.pt:19
-#: src/pyams_file/widget/templates/media-display.pt:49
-#: src/pyams_file/widget/templates/media-display.pt:70
-#: src/pyams_file/widget/templates/file-input.pt:45
-#: src/pyams_file/widget/templates/file-display.pt:21
-msgid "Download"
+#: src/pyams_file/interfaces/__init__.py:116
+msgid "Save file as..."
 msgstr "Enregistrer sous..."
 
-#: src/pyams_file/widget/templates/media-input.pt:54
-#: src/pyams_file/widget/templates/media-display.pt:30
-msgid "Zoom image"
-msgstr "Agrandir l'image"
+#: src/pyams_file/interfaces/__init__.py:117
+msgid "Name under which the file will be saved"
+msgstr "Nom proposé automatiquement lors de l'enregistrement du fichier"
+
+#: src/pyams_file/interfaces/__init__.py:120
+msgid "Language"
+msgstr "Langue"
+
+#: src/pyams_file/interfaces/__init__.py:121
+msgid "File's content language"
+msgstr "Langue du contenu du fichier"
 
 #~ msgid "${size} Kb"
 #~ msgstr "${size} Ko"
--- a/src/pyams_file/locales/pyams_file.pot	Thu Dec 20 09:59:19 2018 +0100
+++ b/src/pyams_file/locales/pyams_file.pot	Thu Dec 20 18:46:31 2018 +0100
@@ -6,7 +6,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-11-28 17:19+0100\n"
+"POT-Creation-Date: 2018-12-20 18:42+0100\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"
@@ -16,82 +16,108 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Lingua 3.10.dev0\n"
 
-#: ./src/pyams_file/image.py:51 ./src/pyams_file/image.py:52
+#: ./src/pyams_file/image.py:49 ./src/pyams_file/image.py:50
 msgid "Default thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/image.py:100
+#: ./src/pyams_file/image.py:98
 msgid "Custom selections"
 msgstr ""
 
-#: ./src/pyams_file/image.py:176
+#: ./src/pyams_file/image.py:174
 msgid "Portrait thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/image.py:186
+#: ./src/pyams_file/image.py:184
 msgid "Square thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/image.py:196
+#: ./src/pyams_file/image.py:194
 msgid "Panoramic thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/image.py:213
+#: ./src/pyams_file/image.py:211
 msgid "Banner thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/image.py:222
+#: ./src/pyams_file/image.py:220
 msgid "Responsive selections"
 msgstr ""
 
-#: ./src/pyams_file/image.py:229
+#: ./src/pyams_file/image.py:227
 msgid "Smartphone thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/image.py:237
+#: ./src/pyams_file/image.py:235
 msgid "Tablet thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/image.py:245
+#: ./src/pyams_file/image.py:243
 msgid "Medium screen thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/image.py:253
+#: ./src/pyams_file/image.py:251
 msgid "Large screen thumbnail"
 msgstr ""
 
-#: ./src/pyams_file/interfaces/__init__.py:110
-msgid "Title"
+#: ./src/pyams_file/widget/templates/media-input.pt:7
+#: ./src/pyams_file/widget/templates/file-input.pt:7
+msgid "Browse..."
 msgstr ""
 
-#: ./src/pyams_file/interfaces/__init__.py:113
-msgid "Description"
+#: ./src/pyams_file/widget/templates/media-input.pt:8
+#: ./src/pyams_file/widget/templates/file-input.pt:8
+msgid "Please select a file..."
+msgstr ""
+
+#: ./src/pyams_file/widget/templates/media-input.pt:19
+#: ./src/pyams_file/widget/templates/file-input.pt:19
+msgid "Delete content"
 msgstr ""
 
-#: ./src/pyams_file/interfaces/__init__.py:116
-msgid "Save file as..."
+#: ./src/pyams_file/widget/templates/media-input.pt:27
+#: ./src/pyams_file/widget/templates/media-input.pt:62
+#: ./src/pyams_file/widget/templates/media-input.pt:93
+#: ./src/pyams_file/widget/templates/file-input.pt:31
+#: ./src/pyams_file/widget/templates/file-display.pt:14
+#: ./src/pyams_file/widget/templates/media-display.pt:10
+#: ./src/pyams_file/widget/templates/media-display.pt:38
+#: ./src/pyams_file/widget/templates/media-display.pt:62
+msgid "Current value:"
 msgstr ""
 
-#: ./src/pyams_file/interfaces/__init__.py:117
-msgid "Name under which the file will be saved"
+#: ./src/pyams_file/widget/templates/media-input.pt:42
+#: ./src/pyams_file/widget/templates/media-input.pt:79
+#: ./src/pyams_file/widget/templates/media-input.pt:108
+#: ./src/pyams_file/widget/templates/file-input.pt:45
+#: ./src/pyams_file/widget/templates/file-display.pt:21
+#: ./src/pyams_file/widget/templates/media-display.pt:19
+#: ./src/pyams_file/widget/templates/media-display.pt:49
+#: ./src/pyams_file/widget/templates/media-display.pt:70
+msgid "Download"
 msgstr ""
 
-#: ./src/pyams_file/interfaces/__init__.py:120
-msgid "Language"
-msgstr ""
-
-#: ./src/pyams_file/interfaces/__init__.py:121
-msgid "File's content language"
+#: ./src/pyams_file/widget/templates/media-input.pt:54
+#: ./src/pyams_file/widget/templates/media-display.pt:30
+msgid "Zoom image"
 msgstr ""
 
 #: ./src/pyams_file/zmi/file.py:42
 msgid "Properties..."
 msgstr ""
 
-#: ./src/pyams_file/zmi/file.py:56
+#: ./src/pyams_file/zmi/file.py:57
 msgid "Update file properties"
 msgstr ""
 
+#: ./src/pyams_file/zmi/file.py:92
+msgid "Edit file..."
+msgstr ""
+
+#: ./src/pyams_file/zmi/file.py:108
+msgid "Edit file content"
+msgstr ""
+
 #: ./src/pyams_file/zmi/image.py:120
 msgid "Rotate image to right..."
 msgstr ""
@@ -261,44 +287,26 @@
 "possible within given limits"
 msgstr ""
 
-#: ./src/pyams_file/widget/templates/media-input.pt:7
-#: ./src/pyams_file/widget/templates/file-input.pt:7
-msgid "Browse..."
+#: ./src/pyams_file/interfaces/__init__.py:110
+msgid "Title"
 msgstr ""
 
-#: ./src/pyams_file/widget/templates/media-input.pt:8
-#: ./src/pyams_file/widget/templates/file-input.pt:8
-msgid "Please select a file..."
+#: ./src/pyams_file/interfaces/__init__.py:113
+msgid "Description"
 msgstr ""
 
-#: ./src/pyams_file/widget/templates/media-input.pt:19
-#: ./src/pyams_file/widget/templates/file-input.pt:19
-msgid "Delete content"
+#: ./src/pyams_file/interfaces/__init__.py:116
+msgid "Save file as..."
 msgstr ""
 
-#: ./src/pyams_file/widget/templates/media-input.pt:27
-#: ./src/pyams_file/widget/templates/media-input.pt:62
-#: ./src/pyams_file/widget/templates/media-input.pt:93
-#: ./src/pyams_file/widget/templates/media-display.pt:10
-#: ./src/pyams_file/widget/templates/media-display.pt:38
-#: ./src/pyams_file/widget/templates/media-display.pt:62
-#: ./src/pyams_file/widget/templates/file-input.pt:31
-#: ./src/pyams_file/widget/templates/file-display.pt:14
-msgid "Current value:"
+#: ./src/pyams_file/interfaces/__init__.py:117
+msgid "Name under which the file will be saved"
 msgstr ""
 
-#: ./src/pyams_file/widget/templates/media-input.pt:42
-#: ./src/pyams_file/widget/templates/media-input.pt:79
-#: ./src/pyams_file/widget/templates/media-input.pt:108
-#: ./src/pyams_file/widget/templates/media-display.pt:19
-#: ./src/pyams_file/widget/templates/media-display.pt:49
-#: ./src/pyams_file/widget/templates/media-display.pt:70
-#: ./src/pyams_file/widget/templates/file-input.pt:45
-#: ./src/pyams_file/widget/templates/file-display.pt:21
-msgid "Download"
+#: ./src/pyams_file/interfaces/__init__.py:120
+msgid "Language"
 msgstr ""
 
-#: ./src/pyams_file/widget/templates/media-input.pt:54
-#: ./src/pyams_file/widget/templates/media-display.pt:30
-msgid "Zoom image"
+#: ./src/pyams_file/interfaces/__init__.py:121
+msgid "File's content language"
 msgstr ""
--- a/src/pyams_file/zmi/file.py	Thu Dec 20 09:59:19 2018 +0100
+++ b/src/pyams_file/zmi/file.py	Thu Dec 20 18:46:31 2018 +0100
@@ -9,33 +9,33 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_utils.interfaces.data import IObjectData
 
 __docformat__ = 'restructuredtext'
 
-
-# import standard library
+from z3c.form import field
+from z3c.form.browser.textarea import TextAreaFieldWidget
+from zope.interface import Interface, implementer, alsoProvides
 
-# import interfaces
 from pyams_file.interfaces import IFile, IFileInfo, IFileModifierForm
+from pyams_file.zmi import FileModifierAction
+from pyams_form.form import ajax_config
+from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.interfaces.viewlet import IContextActions
 from pyams_skin.layer import IPyAMSLayer
-from pyams_utils.interfaces import VIEW_PERMISSION, MANAGE_SYSTEM_PERMISSION
-
-# import packages
-from pyams_file.zmi import FileModifierAction
-from pyams_form.form import AJAXEditForm
-from pyams_pagelet.pagelet import pagelet_config
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogEditForm
-from pyramid.view import view_config
-from z3c.form import field
-from zope.interface import implementer, Interface
 
 from pyams_file import _
 
 
+#
+# File properties form
+#
+
 @viewlet_config(name='file.properties.action', context=IFile, layer=IPyAMSLayer, view=Interface,
-                manager=IContextActions, permission=MANAGE_SYSTEM_PERMISSION, weight=1)
+                manager=IContextActions, weight=1)
 class FilePropertiesAction(FileModifierAction):
     """File properties action"""
 
@@ -46,7 +46,8 @@
     modal_target = True
 
 
-@pagelet_config(name='properties.html', context=IFile, layer=IPyAMSLayer, permission=VIEW_PERMISSION)
+@pagelet_config(name='properties.html', context=IFile, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='properties.json', context=IFile, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 @implementer(IFileModifierForm)
 class FilePropertiesEditForm(AdminDialogEditForm):
     """File properties edit form"""
@@ -57,19 +58,12 @@
     icon_css_class = 'fa fa-fw fa-edit'
 
     fields = field.Fields(IFileInfo)
-    ajax_handler = 'properties.json'
     edit_permission = None
 
     @property
     def title(self):
         return self.context.title or self.context.filename
 
-
-@view_config(name='properties.json', context=IFile, request_type=IPyAMSLayer,
-             permission=VIEW_PERMISSION, renderer='json', xhr=True)
-class FilePropertiesAJAXEditForm(AJAXEditForm, FilePropertiesEditForm):
-    """File properties edit form, AJAX renderer"""
-
     def get_ajax_output(self, changes):
         info_changes = changes.get(IFileInfo, ())
         if ('title' in info_changes) or ('filename' in info_changes):
@@ -78,5 +72,55 @@
                 'message': self.request.localizer.translate(self.successMessage)
             }
         else:
-            return super(FilePropertiesAJAXEditForm, self).get_ajax_output(changes)
+            return super(self.__class__, self).get_ajax_output(changes)
+
+
+#
+# Text file editor
+#
+
+@viewlet_config(name='file.editor.action', context=IFile, layer=IPyAMSLayer, view=Interface,
+                manager=IContextActions, weight=2)
+class FileContentEditorAction(FileModifierAction):
+    """Text file content editor action"""
+
+    def __new__(cls, context, request, view, manager):
+        if not (context.content_type.startswith('text/') or context.content_type.startswith('image/svg')):
+            return None
+        return FileModifierAction.__new__(cls)
+
+    label = _("Edit file...")
+    label_css_class = 'fa fa-fw-md fa-pencil'
+
+    url = 'edit-content.html'
+    modal_target = True
+
 
+@pagelet_config(name='edit-content.html', context=IFile, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='edit-content.json', context=IFile, layer=IPyAMSLayer)
+@implementer(IFileModifierForm)
+class FileContentEditorForm(AdminDialogEditForm):
+    """Text file content edit form"""
+
+    prefix = 'file_content.'
+    dialog_class = 'modal-max'
+
+    legend = _("Edit file content")
+    icon_css_class = 'fa fa-fw fa-pencil'
+    label_css_class = 'control-label col-md-1'
+    input_css_class = 'col-md-11'
+
+    fields = field.Fields(IFile).select('data')
+    fields['data'].widgetFactory = TextAreaFieldWidget
+
+    edit_permission = None
+
+    def updateWidgets(self, prefix=None):
+        super(FileContentEditorForm, self).updateWidgets(prefix)
+        if 'data' in self.widgets:
+            widget = self.widgets['data']
+            widget.widget_css_class = 'input height-500 monospace text-editor'
+            widget.object_data = {
+                'ams-editor-filename': self.context.filename
+            }
+            alsoProvides(widget, IObjectData)