Mercurial > libervia-web
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)