diff browser_side/panels.py @ 351:c943fd54c90e

browser_side: heavy refactorisation for microblogs: - RichTextEditor inheritates from BaseTextEditor - stuff related to display/edition have been moved from MicroblogEntry to LightTextEditor and RichTextEditor. Now the editors has two modes for display/edition and the microblog bubble is actually the editor itself. - RichTextEditor's display mode uses a LightTextEditor (this will be used for WYSIWYG edition) - addressing stuff of RichTextEditor have been moved to a child class RichMessageEditor (used for the rich text editor when clicking on the button left to the unibox) - handle blog titles TODO: - fix encode/decode errors when sending special chars - fix images maximal width in the bubble - rich content WYSIWYG edition
author souliane <souliane@mailoo.org>
date Wed, 12 Feb 2014 15:17:04 +0100
parents f1b9ec412769
children f4efffb9627c
line wrap: on
line diff
--- a/browser_side/panels.py	Wed Feb 12 15:01:33 2014 +0100
+++ b/browser_side/panels.py	Wed Feb 12 15:17:04 2014 +0100
@@ -72,24 +72,24 @@
         enable = self.host.params_ui['unibox']['value']
         self.setVisible(enable)
         if enable and not self.unibox:
-            self.button = Button ('<img src="media/icons/tango/actions/32/format-text-italic.png" class="richTextIcon"/>')
+            self.button = Button('<img src="media/icons/tango/actions/32/format-text-italic.png" class="richTextIcon"/>')
             self.button.setTitle('Open the rich text editor')
             self.button.addStyleName('uniBoxButton')
             self.add(self.button)
             self.unibox = UniBox(self.host)
             self.add(self.unibox)
             self.setCellWidth(self.unibox, '100%')
-            self.button.addClickListener(self.openRichTextEditor)
+            self.button.addClickListener(self.openRichMessageEditor)
             self.unibox.addKey("@@: ")
 
-    def openRichTextEditor(self):
+    def openRichMessageEditor(self):
         """Open the rich text editor."""
         self.button.setVisible(False)
         self.unibox.setVisible(False)
         self.setCellWidth(self.unibox, '0px')
         self.host.panel._contactsMove(self)
 
-        def onCloseCallback():
+        def afterEditCb():
             Window.removeWindowResizeListener(self)
             self.host.panel._contactsMove(self.host.panel._hpanel)
             self.setCellWidth(self.unibox, '100%')
@@ -97,7 +97,7 @@
             self.unibox.setVisible(True)
             self.host.resize()
 
-        richtext.RichTextEditor.getOrCreate(self.host, self, onCloseCallback)
+        richtext.RichMessageEditor.getOrCreate(self.host, self, afterEditCb)
         Window.addWindowResizeListener(self)
         self.host.resize()
 
@@ -336,8 +336,10 @@
         self.id = data['id']
         self.type = data.get('type', 'main_item')
         self.empty = data.get('new', False)
+        self.title = data.get('title', '')
+        self.title_xhtml = data.get('title_xhtml', '')
         self.content = data.get('content', '')
-        self.xhtml = data.get('xhtml', '')
+        self.content_xhtml = data.get('content_xhtml', '')
         self.author = data['author']
         self.updated = float(data.get('updated', 0))  # XXX: int doesn't work here
         try:
@@ -399,21 +401,17 @@
         self.addClickListener(self)
 
         self.pub_data = (self.hash[0], self.hash[1], self.id)
-        self._setContent()
+        self.__setContent()
 
-    def _setContent(self):
+    def __setContent(self):
         """Actually set the entry content (header, icons, bubble...)"""
         self.delete_label = self.update_label = self.comment_label = None
-        self.bubble = self.editbox = self._current_comment = None
-        self._setHeader()
-        if self.empty:
-            self.editable_content = ['', Const.SYNTAX_XHTML]
-        else:
-            self.editable_content = [self.xhtml, Const.SYNTAX_XHTML] if self.xhtml else [self.content, None]
-            self.setEntryDialog()
-        self._setIcons()
+        self.bubble = self._current_comment = None
+        self.__setHeader()
+        self.__setBubble()
+        self.__setIcons()
 
-    def _setHeader(self):
+    def __setHeader(self):
         """Set the entry header"""
         if self.empty:
             return
@@ -427,7 +425,7 @@
                                             }
                             )
 
-    def _setIcons(self):
+    def __setIcons(self):
         """Set the entry icons (delete, update, comment)"""
         if self.empty:
             return
@@ -456,128 +454,83 @@
         elif sender == self.delete_label:
             self._delete()
         elif sender == self.update_label:
-            self.setEntryDialog(edit=True)
+            self.bubble.edit(True)
         elif sender == self.comment_label:
             self._comment()
 
-    def onKeyUp(self, sender, keycode, modifiers):
-        """Update is done when ENTER key is pressed within the raw editbox"""
-        if sender != self.editbox or not self.editbox.getVisible():
-            return
-        if keycode == KEY_ENTER:
-            self._updateContent()
-
-    def onLostFocus(self, sender):
-        """Update is done when the focus leaves the raw editbox"""
-        if sender != self.editbox or not self.editbox.getVisible():
-            return
-        self._updateContent()
-
-    def _updateContent(self, cancel=False):
-        """Send the new content to the backend, remove the entry if it was
-        an empty one (used for creating a new blog post)"""
-        if not self.editbox or not self.editbox.getVisible():
-            return
-        self.entry_dialog.setWidth("auto")
-        self.entry_dialog.remove(self.edit_panel)
-        self.entry_dialog.add(self.bubble)
-        new_text = self.editbox.getText().strip()
-        self.edit_panel = self.editbox = None
-
-        def removeNewEntry():
-            if self.empty:
-                self._blog_panel.removeEntry(self.type, self.id)
-                if self.type == 'main_item':  # restore the "New message" button
-                    self._blog_panel.setUniBox(enable=False)
-                else:  # allow to create a new comment
-                    self._parent_entry._current_comment = None
-
-        if cancel or new_text == self.editable_content[0] or new_text == "":
-            removeNewEntry()
-            return
-        self.editable_content[0] = new_text
+    def __modifiedCb(self, content):
+        """Send the new content to the backend
+        @return: False to restore the original content if a deletion has been cancelled
+        """
+        if not content['text']:  # previous content has been emptied
+            self._delete(True)
+            return False
         extra = {'published': str(self.published)}
-        if self.empty or self.xhtml:
-            extra.update({'rich': new_text})
+        if self.empty or self.content_xhtml:
+            # TODO: if the user change his parameters after the message edition started,
+            # the message syntax could be different then the current syntax: pass the
+            # message syntax in extra for the frontend to use it instead of current syntax.
+            extra.update({'content_rich': content['text'], 'title': content['title']})
         if self.empty:
             if self.type == 'main_item':
-                self._blog_panel.host.bridge.call('sendMblog', None, None, self._blog_panel.accepted_groups, new_text, extra)
-            else:
-                self._blog_panel.host.bridge.call('sendMblogComment', None, self._parent_entry.comments, new_text, extra)
-        else:
-            self._blog_panel.host.bridge.call('updateMblog', None, self.pub_data, self.comments, new_text, extra)
-        removeNewEntry()
-
-    def setEntryDialog(self, edit=False):
-        """Set the bubble or the editor
-        @param edit: set to True to display the editor"""
-        if edit:
-            if self.editbox and self.editbox.getVisible():
-                self.editbox.setFocus(True)
-                return
-            if self.empty or self.xhtml:
-                def cb(result):
-                    self._updateContent(result == richtext.CANCEL)
-
-                options = ['no_recipient', 'no_sync_unibox', 'no_style', 'no_close']
-                if not self.empty:
-                    options.append('update_msg')
-                editor = richtext.RichTextEditor(self._blog_panel.host, self.panel, cb, options=options)
-                editor.setWidth('100%')
-                self.editbox = editor.textarea
-                editor.setVisible(True)  # needed to build the toolbar
-                if self.editable_content[0]:
-                    self._blog_panel.host.bridge.call('syntaxConvert', lambda d: self._setOriginalText(d, editor),
-                                                      self.editable_content[0], self.editable_content[1])
-                else:
-                    self._setOriginalText("", editor)
+                self._blog_panel.host.bridge.call('sendMblog', None, None, self._blog_panel.accepted_groups, content['text'], extra)
             else:
-                if not self.editbox:
-                    self.editbox = TextArea()
-                    self.editbox.addFocusListener(self)
-                    self.editbox.addKeyboardListener(self)
-                self._setOriginalText(self.editable_content[0], self.editbox)
+                self._blog_panel.host.bridge.call('sendMblogComment', None, self._parent_entry.comments, content['text'], extra)
         else:
-            if not self.bubble:
-                self.bubble = HTML()
-                self.bubble.setStyleName("bubble")
+            self._blog_panel.host.bridge.call('updateMblog', None, self.pub_data, self.comments, content['text'], extra)
+        return True
 
-            self.bubble.setHTML(addURLToText(html_sanitize(self.content)) if not self.xhtml else self.xhtml)
-            self.entry_dialog.add(self.bubble)
+    def __afterEditCb(self, content):
+        """Remove the entry if it was an empty one (used for creating a new blog post).
+        Data for the actual new blog post will be received from the bridge"""
+        if self.empty:
+            self._blog_panel.removeEntry(self.type, self.id)
+            if self.type == 'main_item':  # restore the "New message" button
+                self._blog_panel.refresh()
+            else:  # allow to create a new comment
+                self._parent_entry._current_comment = None
 
-    def _setOriginalText(self, text, container):
-        """Set the original text to be modified in the editor"""
-        text = text.strip()
-        container.original_text = text
-        self.editbox.setWidth('100%')
-        self.editbox.setText(text)
-        panel = SimplePanel()
-        panel.add(container)
-        panel.setStyleName("bubble")
-        panel.addStyleName('bubble-editbox')
-        if self.bubble:
-            self.entry_dialog.remove(self.bubble)
-        self.entry_dialog.add(panel)
-        self.editbox.setFocus(True)
-        if text:
-            self.editbox.setSelectionRange(len(text), 0)
-        self.edit_panel = panel
-        self.editable_content = [text, container.format if isinstance(container, richtext.RichTextEditor) else None]
+    def __setBubble(self):
+        """Set the bubble displaying the initial content."""
+        content = {'text': self.content_xhtml if self.content_xhtml else self.content,
+                   'title': self.title_xhtml if self.title_xhtml else self.title}
+        if self.empty or self.content_xhtml:  # new message and rich text message
+            content.update({'syntax': Const.SYNTAX_XHTML})
+            if self.author != self._blog_panel.host.whoami.bare:
+                options = ['read_only']
+            else:
+                options = [] if self.empty else ['update_msg']
+            self.bubble = richtext.RichTextEditor(self._blog_panel.host, content, self.__modifiedCb, self.__afterEditCb, options)
+        else:  # assume raw text message have no title
+            self.bubble = LightTextEditor(content, self.__modifiedCb, self.__afterEditCb, True)
+        self.bubble.setStyleName("bubble")
+        self.entry_dialog.add(self.bubble)
+        self.bubble.edit(False)
 
-    def _delete(self):
-        """Ask confirmation for deletion"""
+    def _delete(self, empty=False):
+        """Ask confirmation for deletion.
+        @return: False if the deletion has been cancelled."""
         def confirm_cb(answer):
             if answer:
                 self._blog_panel.host.bridge.call('deleteMblog', None, self.pub_data, self.comments)
+            else:  # restore the text if it has been emptied during the edition
+                self.bubble.setContent(self.bubble._original_content)
 
-        target = 'message and all its comments' if self.comments else 'comment'
-        _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to delete this %s?" % target)
-        _dialog.show()
+        if self.empty:
+            text = _("New ") + (_("message") if self.comments else _("comment")) + _(" without body has been cancelled.")
+            dialog.InfoDialog(_("Information"), text).show()
+            return
+        text = ""
+        if empty:
+            text = (_("Message") if self.comments else _("Comment")) + _(" body has been emptied.<br/>")
+        target = _('message and all its comments') if self.comments else _('comment')
+        text += _("Do you really want to delete this %s?") % target
+        dialog.ConfirmDialog(confirm_cb, text=text).show()
 
     def _comment(self):
         """Add an empty entry for a new comment"""
         if self._current_comment:
-            self._current_comment.editbox.setFocus(True)
+            self._current_comment.bubble.setFocus(True)
             return
         data = {'id': str(time()),
                 'new': True,
@@ -589,7 +542,7 @@
         entry = self._blog_panel.addEntry(data)
         entry._parent_entry = self
         self._current_comment = entry
-        entry.setEntryDialog(edit=True)
+        entry.bubble.edit(True)
 
 
 class MicroblogPanel(base_widget.LiberviaWidget):
@@ -625,7 +578,7 @@
                         'author': self.host.whoami.bare,
                         }
                 entry = self.addEntry(data)
-                entry.setEntryDialog(edit=True)
+                entry.bubble.edit(True)
             self.new_button = Button("New message", listener=addBox)
             self.new_button.setStyleName("microblogNewButton")
             self.vpanel.insert(self.new_button, 0)