Back to index

moin  1.9.0~rc2
PageGraphicalEditor.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - Call the GUI editor (FCKeditor)
00004 
00005     Same as PageEditor, but we use the HTML based GUI editor here.
00006 
00007     TODO:
00008     * see PageEditor.py
00009 
00010     @copyright: 2006 Bastian Blank, Florian Festi,
00011                 2006-2007 MoinMoin:ThomasWaldmann
00012     @license: GNU GPL, see COPYING for details.
00013 """
00014 import re
00015 
00016 from MoinMoin import PageEditor
00017 from MoinMoin import wikiutil
00018 from MoinMoin.Page import Page
00019 from MoinMoin.widget import html
00020 from MoinMoin.widget.dialog import Status
00021 from MoinMoin.util import web
00022 from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
00023 
00024 def execute(pagename, request):
00025     if not request.user.may.write(pagename):
00026         _ = request.getText
00027         request.theme.add_msg_('You are not allowed to edit this page.', "error")
00028         Page(request, pagename).send_page()
00029         return
00030 
00031     PageGraphicalEditor(request, pagename).sendEditor()
00032 
00033 
00034 class PageGraphicalEditor(PageEditor.PageEditor):
00035     """ Same as PageEditor, but use the GUI editor (FCKeditor) """
00036     def word_rule(self):
00037         regex = re.compile(r"\(\?<![^)]*?\)")
00038         word_rule = regex.sub("", WikiParser.word_rule_js)
00039         return repr(word_rule)[1:]
00040 
00041     def sendEditor(self, **kw):
00042         """ Send the editor form page.
00043 
00044         @keyword preview: if given, show this text in preview mode
00045         @keyword staytop: don't go to #preview
00046         @keyword comment: comment field (when preview is true)
00047         """
00048         from MoinMoin import i18n
00049         from MoinMoin.action import SpellCheck
00050 
00051         request = self.request
00052         form = request.form
00053         _ = self._
00054 
00055         raw_body = ''
00056         msg = None
00057         conflict_msg = None
00058         edit_lock_message = None
00059         preview = kw.get('preview', None)
00060         staytop = kw.get('staytop', 0)
00061 
00062         # check edit permissions
00063         if not request.user.may.write(self.page_name):
00064             msg = _('You are not allowed to edit this page.')
00065         elif not self.isWritable():
00066             msg = _('Page is immutable!')
00067         elif self.rev:
00068             # Trying to edit an old version, this is not possible via
00069             # the web interface, but catch it just in case...
00070             msg = _('Cannot edit old revisions!')
00071         else:
00072             # try to acquire edit lock
00073             ok, edit_lock_message = self.lock.acquire()
00074             if not ok:
00075                 # failed to get the lock
00076                 if preview is not None:
00077                     edit_lock_message = _('The lock you held timed out. Be prepared for editing conflicts!'
00078                         ) + "<br>" + edit_lock_message
00079                 else:
00080                     msg = edit_lock_message
00081 
00082         # Did one of the prechecks fail?
00083         if msg:
00084             request.theme.add_msg(msg, "error")
00085             self.send_page()
00086             return
00087 
00088         # Emit http_headers after checks (send_page)
00089         request.disableHttpCaching(level=2)
00090 
00091         # check if we want to load a draft
00092         use_draft = None
00093         if 'button_load_draft' in form:
00094             wanted_draft_timestamp = int(form.get('draft_ts', '0'))
00095             if wanted_draft_timestamp:
00096                 draft = self._load_draft()
00097                 if draft is not None:
00098                     draft_timestamp, draft_rev, draft_text = draft
00099                     if draft_timestamp == wanted_draft_timestamp:
00100                         use_draft = draft_text
00101 
00102         # Check for draft / normal / preview submit
00103         if use_draft is not None:
00104             title = _('Draft of "%(pagename)s"')
00105             # Propagate original revision
00106             rev = int(form['draft_rev'])
00107             self.set_raw_body(use_draft, modified=1)
00108             preview = use_draft
00109         elif preview is None:
00110             title = _('Edit "%(pagename)s"')
00111         else:
00112             title = _('Preview of "%(pagename)s"')
00113             # Propagate original revision
00114             rev = request.rev
00115             self.set_raw_body(preview, modified=1)
00116 
00117         # send header stuff
00118         lock_timeout = self.lock.timeout / 60
00119         lock_page = wikiutil.escape(self.page_name, quote=1)
00120         lock_expire = _("Your edit lock on %(lock_page)s has expired!") % {'lock_page': lock_page}
00121         lock_mins = _("Your edit lock on %(lock_page)s will expire in # minutes.") % {'lock_page': lock_page}
00122         lock_secs = _("Your edit lock on %(lock_page)s will expire in # seconds.") % {'lock_page': lock_page}
00123 
00124         # get request parameters
00125         try:
00126             text_rows = int(form['rows'])
00127         except StandardError:
00128             text_rows = self.cfg.edit_rows
00129             if request.user.valid:
00130                 text_rows = int(request.user.edit_rows)
00131 
00132         if preview is not None:
00133             # Check for editing conflicts
00134             if not self.exists():
00135                 # page does not exist, are we creating it?
00136                 if rev:
00137                     conflict_msg = _('Someone else deleted this page while you were editing!')
00138             elif rev != self.current_rev():
00139                 conflict_msg = _('Someone else changed this page while you were editing!')
00140                 if self.mergeEditConflict(rev):
00141                     conflict_msg = _("""Someone else saved this page while you were editing!
00142 Please review the page and save then. Do not save this page as it is!""")
00143                     rev = self.current_rev()
00144             if conflict_msg:
00145                 # We don't show preview when in conflict
00146                 preview = None
00147 
00148         elif self.exists():
00149             # revision of existing page
00150             rev = self.current_rev()
00151         else:
00152             # page creation
00153             rev = 0
00154 
00155         self.setConflict(bool(conflict_msg))
00156 
00157         # Page editing is done using user language
00158         request.setContentLanguage(request.lang)
00159 
00160         # Get the text body for the editor field.
00161         # TODO: what about deleted pages? show the text of the last revision or use the template?
00162         if preview is not None:
00163             raw_body = self.get_raw_body()
00164             if use_draft:
00165                 request.write(_("[Content loaded from draft]"), '<br>')
00166         elif self.exists():
00167             # If the page exists, we get the text from the page.
00168             # TODO: maybe warn if template argument was ignored because the page exists?
00169             raw_body = self.get_raw_body()
00170         elif 'template' in request.values:
00171             # If the page does not exist, we try to get the content from the template parameter.
00172             template_page = wikiutil.unquoteWikiname(request.values['template'])
00173             if request.user.may.read(template_page):
00174                 raw_body = Page(request, template_page).get_raw_body()
00175                 if raw_body:
00176                     request.write(_("[Content of new page loaded from %s]") % (template_page, ), '<br>')
00177                 else:
00178                     request.write(_("[Template %s not found]") % (template_page, ), '<br>')
00179             else:
00180                 request.write(_("[You may not read %s]") % (template_page, ), '<br>')
00181 
00182         # Make backup on previews - but not for new empty pages
00183         if not use_draft and preview and raw_body:
00184             self._save_draft(raw_body, rev)
00185 
00186         draft_message = None
00187         loadable_draft = False
00188         if preview is None:
00189             draft = self._load_draft()
00190             if draft is not None:
00191                 draft_timestamp, draft_rev, draft_text = draft
00192                 if draft_text != raw_body:
00193                     loadable_draft = True
00194                     page_rev = rev
00195                     draft_timestamp_str = request.user.getFormattedDateTime(draft_timestamp)
00196                     draft_message = _(u"'''<<BR>>Your draft based on revision %(draft_rev)d (saved %(draft_timestamp_str)s) can be loaded instead of the current revision %(page_rev)d by using the load draft button - in case you lost your last edit somehow without saving it.''' A draft gets saved for you when you do a preview, cancel an edit or unsuccessfully save.", wiki=True, percent=True) % locals()
00197 
00198         # Setup status message
00199         status = [kw.get('msg', ''), conflict_msg, edit_lock_message, draft_message]
00200         status = [msg for msg in status if msg]
00201         status = ' '.join(status)
00202         status = Status(request, content=status)
00203 
00204         request.theme.add_msg(status, "error")
00205         request.theme.send_title(
00206             title % {'pagename': self.split_title(), },
00207             page=self,
00208             html_head=self.lock.locktype and (
00209                 PageEditor._countdown_js % {
00210                      'countdown_script': request.theme.externalScript('countdown'),
00211                      'lock_timeout': lock_timeout,
00212                      'lock_expire': lock_expire,
00213                      'lock_mins': lock_mins,
00214                      'lock_secs': lock_secs,
00215                     }) or '',
00216             editor_mode=1,
00217         )
00218 
00219         request.write(request.formatter.startContent("content"))
00220 
00221         # Generate default content for new pages
00222         if not raw_body:
00223             raw_body = _('Describe %s here.') % (self.page_name, )
00224 
00225         # send form
00226         request.write('<form id="editor" method="post" action="%s#preview">' % (
00227                 request.href(self.page_name)
00228             ))
00229 
00230         # yet another weird workaround for broken IE6 (it expands the text
00231         # editor area to the right after you begin to type...). IE sucks...
00232         # http://fplanque.net/2003/Articles/iecsstextarea/
00233         request.write('<fieldset style="border:none;padding:0;">')
00234 
00235         request.write(unicode(html.INPUT(type="hidden", name="action", value="edit")))
00236 
00237         # Send revision of the page our edit is based on
00238         request.write('<input type="hidden" name="rev" value="%d">' % (rev, ))
00239 
00240         # Add src format (e.g. 'wiki') into a hidden form field, so that
00241         # we can load the correct converter after POSTing.
00242         request.write('<input type="hidden" name="format" value="%s">' % self.pi['format'])
00243 
00244         # Create and send a ticket, so we can check the POST
00245         request.write('<input type="hidden" name="ticket" value="%s">' % wikiutil.createTicket(request))
00246 
00247         # Save backto in a hidden input
00248         backto = request.values.get('backto')
00249         if backto:
00250             request.write(unicode(html.INPUT(type="hidden", name="backto", value=backto)))
00251 
00252         # button bar
00253         button_spellcheck = '<input class="button" type="submit" name="button_spellcheck" value="%s">' % _('Check Spelling')
00254 
00255         save_button_text = _('Save Changes')
00256         cancel_button_text = _('Cancel')
00257 
00258         if self.cfg.page_license_enabled:
00259             request.write('<p><em>', _(
00260 """By hitting '''%(save_button_text)s''' you put your changes under the %(license_link)s.
00261 If you don't want that, hit '''%(cancel_button_text)s''' to cancel your changes.""", wiki=True) % {
00262                 'save_button_text': save_button_text,
00263                 'cancel_button_text': cancel_button_text,
00264                 'license_link': wikiutil.getLocalizedPage(request, self.cfg.page_license_page).link_to(request),
00265             }, '</em></p>')
00266 
00267         request.write('''
00268 <input class="button" type="submit" name="button_save" value="%s">
00269 <input class="button" type="submit" name="button_preview" value="%s">
00270 <input class="button" type="submit" name="button_switch" value="%s">
00271 ''' % (save_button_text, _('Preview'), _('Text mode'), ))
00272 
00273         if loadable_draft:
00274             request.write('''
00275 <input class="button" type="submit" name="button_load_draft" value="%s" onClick="flgChange = false;">
00276 <input type="hidden" name="draft_ts" value="%d">
00277 <input type="hidden" name="draft_rev" value="%d">
00278 ''' % (_('Load Draft'), draft_timestamp, draft_rev))
00279 
00280         request.write('''
00281 %s
00282 <input class="button" type="submit" name="button_cancel" value="%s">
00283 <input type="hidden" name="editor" value="gui">
00284 ''' % (button_spellcheck, cancel_button_text, ))
00285         if self.cfg.mail_enabled:
00286             request.write('''
00287 <script type="text/javascript">
00288     function toggle_trivial(CheckedBox)
00289     {
00290         TrivialBoxes = document.getElementsByName("trivial");
00291         for (var i = 0; i < TrivialBoxes.length; i++)
00292             TrivialBoxes[i].checked = CheckedBox.checked;
00293     }
00294 </script>
00295 &nbsp;
00296 <input type="checkbox" name="trivial" id="chktrivialtop" value="1" %(checked)s onclick="toggle_trivial(this)">
00297 <label for="chktrivialtop">%(label)s</label>
00298 ''' % {
00299           'checked': ('', 'checked')[form.get('trivial', '0') == '1'],
00300           'label': _("Trivial change"),
00301        })
00302 
00303         from MoinMoin.security.textcha import TextCha
00304         request.write(TextCha(request).render())
00305 
00306         self.sendconfirmleaving() # TODO update state of flgChange to make this work, see PageEditor
00307 
00308         # Add textarea with page text
00309         lang = self.pi.get('language', request.cfg.language_default)
00310         contentlangdirection = i18n.getDirection(lang) # 'ltr' or 'rtl'
00311         uilanguage = request.lang
00312         url_prefix_static = request.cfg.url_prefix_static
00313         url_prefix_local = request.cfg.url_prefix_local
00314         wikipage = wikiutil.quoteWikinameURL(self.page_name)
00315         fckbasepath = request.cfg.url_prefix_fckeditor
00316         wikiurl = request.script_root + '/'
00317         themepath = '%s/%s' % (url_prefix_static, request.theme.name)
00318         smileypath = themepath + '/img'
00319         # auto-generating a list for SmileyImages does NOT work from here!
00320         editor_size = int(request.user.edit_rows) * 22 # 22 height_pixels/line
00321         word_rule = self.word_rule()
00322 
00323         request.write("""
00324 <script type="text/javascript" src="%(fckbasepath)s/fckeditor.js"></script>
00325 <script type="text/javascript">
00326 <!--
00327     var oFCKeditor = new FCKeditor( 'savetext', '100%%', %(editor_size)s, 'MoinDefault' ) ;
00328     oFCKeditor.BasePath= '%(fckbasepath)s/' ;
00329     oFCKeditor.Config['WikiBasePath'] = '%(wikiurl)s' ;
00330     oFCKeditor.Config['WikiPage'] = '%(wikipage)s' ;
00331     oFCKeditor.Config['PluginsPath'] = '%(url_prefix_local)s/applets/moinFCKplugins/' ;
00332     oFCKeditor.Config['CustomConfigurationsPath'] = '%(url_prefix_local)s/applets/moinfckconfig.js'  ;
00333     oFCKeditor.Config['WordRule'] = %(word_rule)s ;
00334     oFCKeditor.Config['SmileyPath'] = '%(smileypath)s/' ;
00335     oFCKeditor.Config['EditorAreaCSS'] = '%(themepath)s/css/common.css' ;
00336     oFCKeditor.Config['SkinPath'] = '%(fckbasepath)s/editor/skins/silver/' ;
00337     oFCKeditor.Config['AutoDetectLanguage'] = false ;
00338     oFCKeditor.Config['DefaultLanguage'] = '%(uilanguage)s' ;
00339     oFCKeditor.Config['ContentLangDirection']  = '%(contentlangdirection)s' ;
00340     oFCKeditor.Value= """ % locals())
00341 
00342         from MoinMoin.formatter.text_gedit import Formatter
00343         self.formatter = Formatter(request)
00344         self.formatter.page = self
00345         output = request.redirectedOutput(self.send_page_content, request, raw_body, format=self.pi['format'], do_cache=False)
00346         output = repr(output)
00347         if output[0] == 'u':
00348             output = output[1:]
00349         request.write(output)
00350         request.write(""" ;
00351     oFCKeditor.Create() ;
00352 //-->
00353 </script>
00354 """)
00355         request.write("<p>")
00356         request.write(_("Comment:"),
00357             ' <input id="editor-comment" type="text" name="comment" value="%s" size="80" maxlength="200">' % (
00358                 wikiutil.escape(kw.get('comment', ''), 1), ))
00359         request.write("</p>")
00360 
00361         # Category selection
00362         filterfn = self.cfg.cache.page_category_regexact.search
00363         cat_pages = request.rootpage.getPageList(filter=filterfn)
00364         cat_pages.sort()
00365         cat_pages = [wikiutil.pagelinkmarkup(p) for p in cat_pages]
00366         cat_pages.insert(0, ('', _('<No addition>')))
00367         request.write("<p>")
00368         request.write(_('Add to: %(category)s') % {
00369             'category': unicode(web.makeSelection('category', cat_pages)),
00370         })
00371         if self.cfg.mail_enabled:
00372             request.write('''
00373 &nbsp;
00374 <input type="checkbox" name="trivial" id="chktrivial" value="1" %(checked)s onclick="toggle_trivial(this)">
00375 <label for="chktrivial">%(label)s</label> ''' % {
00376                 'checked': ('', 'checked')[form.get('trivial', '0') == '1'],
00377                 'label': _("Trivial change"),
00378                 })
00379 
00380         request.write('''
00381 &nbsp;
00382 <input type="checkbox" name="rstrip" id="chkrstrip" value="1" %(checked)s>
00383 <label for="chkrstrip">%(label)s</label>
00384 </p> ''' % {
00385             'checked': ('', 'checked')[form.get('rstrip', '0') == '1'],
00386             'label': _('Remove trailing whitespace from each line')
00387             })
00388 
00389         request.write("</p>")
00390 
00391         badwords_re = None
00392         if preview is not None:
00393             if 'button_spellcheck' in form or 'button_newwords' in form:
00394                 badwords, badwords_re, msg = SpellCheck.checkSpelling(self, request, own_form=0)
00395                 request.write("<p>%s</p>" % msg)
00396         request.write('</fieldset>')
00397         request.write("</form>")
00398 
00399         if preview is not None:
00400             if staytop:
00401                 content_id = 'previewbelow'
00402             else:
00403                 content_id = 'preview'
00404             self.send_page(content_id=content_id, content_only=1, hilite_re=badwords_re)
00405 
00406         request.write(request.formatter.endContent()) # end content div
00407         request.theme.send_footer(self.page_name)
00408         request.theme.send_closing_html()
00409