changeset 1850:3d38db210672

action CopyPage added
author Reimar Bauer <rb.proj AT googlemail DOT com>
date Sat, 03 Mar 2007 10:47:29 +0100
parents ff8a6ed6d7aa
children d4274455706a
files MoinMoin/PageEditor.py MoinMoin/action/CopyPage.py MoinMoin/theme/__init__.py docs/CHANGES
diffstat 4 files changed, 238 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/PageEditor.py	Sat Mar 03 00:17:41 2007 +0100
+++ b/MoinMoin/PageEditor.py	Sat Mar 03 10:47:29 2007 +0100
@@ -1,4 +1,3 @@
-# -*- coding: iso-8859-1 -*-
 """
     MoinMoin - PageEditor class
 
@@ -487,6 +486,64 @@
         page = backto and Page(request, backto) or self
         page.send_page(msg=_('Edit was cancelled.'))
 
+    def copyPage(self, newpagename, comment=None):
+        """
+        Copy the current version of the page (keeping the backups, logs and attachments).
+        
+        @param comment: Comment given by user
+        @rtype: unicode
+        @return: success flag, error message
+        """
+        request = self.request
+        _ = self._
+
+        if not newpagename:
+            return False, _("You can't copy to an empty pagename.")
+
+        newpage = PageEditor(request, newpagename)
+
+        pageexists_error = _("""'''A page with the name {{{'%s'}}} already exists.'''
+Try a different name.""") % (newpagename,)
+
+        # Check whether a page with the new name already exists
+        if newpage.exists(includeDeleted=1):
+            return False, pageexists_error
+
+        # Get old page text
+        savetext = self.get_raw_body()
+
+        oldpath = self.getPagePath(check_create=0)
+        newpath = newpage.getPagePath(check_create=0)
+
+        # Copy page
+        # NOTE: might fail if another process created newpagename just
+        try:
+            filesys.copytree(oldpath, newpath)
+            self.error = None
+
+            if request.user.may.write(newpagename):
+                # If current user has write access
+                # Save page text with a comment about the old name
+                #
+                # TODO: acl read protected pages are copied too but they will be not shown
+                #       in RecentChanges, because current user is not able to write a comment
+                savetext = u"## page was copied from %s\n%s" % (self.page_name, savetext)
+                newpage.saveText(savetext, 0, comment=comment, index=0, extra=self.page_name, action='SAVE')
+
+            if request.cfg.xapian_search:
+                from MoinMoin.search.Xapian import Index
+                index = Index(request)
+                if index.exists():
+                    index.update_page(newpagename)
+            return True, None
+        except OSError, err:
+            # Try to understand what happened. Maybe its better to check
+            # the error code, but I just reused the available code above...
+            if newpage.exists(includeDeleted=1):
+                return False, pageexists_error
+            else:
+                return False, _('Could not copy page because of file system error: %s.') % unicode(err)
+
     def renamePage(self, newpagename, comment=None):
         """
         Rename the current version of the page (making a backup before deletion
@@ -629,7 +686,7 @@
         else:
             querystr = {}
         pagelink = request.getQualifiedURL(self.url(request, querystr, relative=False))
- 
+
         mailBody = _("Dear Wiki user,\n\n"
             'You have subscribed to a wiki page or wiki category on "%(sitename)s" for change notification.\n\n'
             "The following page has been changed by %(editor)s:\n"
@@ -859,7 +916,8 @@
 
         return pragmas
 
-    def copypage(self):
+    def copy_underlay_page(self):
+        # renamed from copypage to avoid conflicts with copyPage
         """
         Copy a page from underlay directory to page directory
         """
@@ -890,7 +948,7 @@
         #is_deprecated = self._get_pragmas(text).has_key("deprecated")
         was_deprecated = self._get_pragmas(self.get_raw_body()).has_key("deprecated")
 
-        self.copypage()
+        self.copy_underlay_page()
 
         # remember conflict state
         self.setConflict(wikiutil.containsConflictMarker(text))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/CopyPage.py	Sat Mar 03 10:47:29 2007 +0100
@@ -0,0 +1,170 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - CopyPage action
+
+    This action allows you to copy a page.
+
+    @copyright: 2007 ReimarBauer
+    @license: GNU GPL, see COPYING for details.
+"""
+import re
+from MoinMoin import wikiutil
+from MoinMoin.Page import Page
+from MoinMoin.PageEditor import PageEditor
+from MoinMoin.action import ActionBase
+
+class CopyPage(ActionBase):
+    """ Copy page action
+
+    Note: the action name is the class name
+    """
+    def __init__(self, pagename, request):
+        ActionBase.__init__(self, pagename, request)
+        self.use_ticket = True
+        _ = self._
+        self.form_trigger = 'copy'
+        self.form_trigger_label = _('Copy Page')
+        filterfn = re.compile(pagename).match
+        pages = request.rootpage.getPageList(user='', exists=1, filter=filterfn)
+        self.subpages = []
+        self.users_subpages = []
+        subpage = pagename + '/'
+        for name in pages:
+            if name.startswith(subpage):
+                self.subpages.append(name)
+                if self.request.user.may.read(name):
+                    self.users_subpages.append(name)
+
+    def is_allowed(self):
+        may = self.request.user.may
+        return may.read(self.pagename)
+
+    def check_condition(self):
+        _ = self._
+        if not self.page.exists():
+            return _('This page is already deleted or was never created!')
+        else:
+            return None
+
+    def do_action(self):
+        """ copy this page to "pagename" """
+        _ = self._
+        form = self.form
+        newpagename = form.get('newpagename', [u''])[0]
+        newpagename = self.request.normalizePagename(newpagename)
+        comment = form.get('comment', [u''])[0]
+        comment = wikiutil.clean_comment(comment)
+
+        self.page = PageEditor(self.request, self.pagename)
+        success, msg = self.page.copyPage(newpagename, comment)
+
+        copy_subpages = 0
+        if form.has_key('copy_subpages'):
+            try:
+                copy_subpages = int(form['copy_subpages'][0])
+            except:
+                pass
+
+        if copy_subpages and self.subpages or (not self.users_subpages and self.subpages):
+            for name in self.subpages:
+                self.page = PageEditor(self.request, name)
+                new_subpagename = name.replace(self.pagename, newpagename, 1)
+                success_i, msg = self.page.copyPage(new_subpagename, comment)
+
+        self.newpagename = newpagename # keep there for finish
+        return success, msg
+
+    def do_action_finish(self, success):
+        if success:
+            url = Page(self.request, self.newpagename).url(self.request, relative=False)
+            self.request.http_redirect(url)
+            self.request.finish()
+        else:
+            self.render_msg(self.make_form())
+
+    def get_form_html(self, buttons_html):
+        _ = self._
+        if self.users_subpages:
+            subpages = ' '.join(self.users_subpages)
+
+            d = {
+                'subpage': subpages,
+                'subpages_checked':('', 'checked')[self.request.form.get('subpages_checked', ['0'])[0] == '1'],
+                'subpage_label': _('Copy all /subpages too?'),
+                'pagename': wikiutil.escape(self.pagename),
+                'newname_label': _("New name"),
+                'comment_label': _("Optional reason for the copying"),
+                'buttons_html': buttons_html,
+                'querytext': _('Really copy this page?')
+                }
+
+            return '''
+<strong>%(querytext)s</strong>
+<br>
+<br>
+<table>
+    <tr>
+    <dd>
+        %(subpage_label)s<input type="checkbox" name="copy_subpages" value="1" %(subpages_checked)s> 
+    </dd>
+    <dd>
+        <class="label"><subpage> %(subpage)s</subpage>
+    </dd>   
+    </tr>
+</table>       
+<table>
+    <tr>
+        <td class="label"><label>%(newname_label)s</label></td>
+        <td class="content">
+            <input type="text" name="newpagename" value="%(pagename)s">
+        </td>
+    </tr>
+    <tr>
+        <td class="label"><label>%(comment_label)s</label></td>
+        <td class="content">
+            <input type="text" name="comment" maxlength="200">
+        </td>
+    </tr>
+    <tr>
+        <td></td>
+        <td class="buttons">
+            %(buttons_html)s
+        </td>
+    </tr>
+</table>
+''' % d
+
+        else:
+            d = {
+                'pagename': wikiutil.escape(self.pagename),
+                'newname_label': _("New name"),
+                'comment_label': _("Optional reason for the copying"),
+                'buttons_html': buttons_html,
+                }
+            return '''
+<table>
+    <tr>
+        <td class="label"><label>%(newname_label)s</label></td>
+        <td class="content">
+            <input type="text" name="newpagename" value="%(pagename)s">
+        </td>
+    </tr>
+    <tr>
+        <td class="label"><label>%(comment_label)s</label></td>
+        <td class="content">
+            <input type="text" name="comment" maxlength="200">
+        </td>
+    </tr>
+    <tr>
+        <td></td>
+        <td class="buttons">
+            %(buttons_html)s
+        </td>
+    </tr>
+</table>
+''' % d
+
+def execute(pagename, request):
+    """ Glue code for actions """
+    CopyPage(pagename, request).render()
+
--- a/MoinMoin/theme/__init__.py	Sat Mar 03 00:17:41 2007 +0100
+++ b/MoinMoin/theme/__init__.py	Sat Mar 03 10:47:29 2007 +0100
@@ -314,7 +314,7 @@
         request = self.request
         fmt = request.formatter
         title = None
-        
+
         # Handle [pagename title] or [url title] formats
         if text.startswith('[') and text.endswith(']'):
             text = text[1:-1].strip()
@@ -363,7 +363,7 @@
             page = wikiutil.getLocalizedPage(request, pagename)
         else:
             page = Page(request, pagename)
-            
+
         if not title:
             title = page.split_title()
             title = self.shortenPagename(title)
@@ -900,6 +900,7 @@
             'LocalSiteMap',
             '__separator__',
             'RenamePage',
+            'CopyPage',
             'DeletePage',
             '__separator__',
             'MyPages',
@@ -921,6 +922,7 @@
             'refresh': _('Delete Cache', formatted=False),
             'SpellCheck': _('Check Spelling', formatted=False), # rename action!
             'RenamePage': _('Rename Page', formatted=False),
+            'CopyPage': _('Copy Page', formatted=False),
             'DeletePage': _('Delete Page', formatted=False),
             'LikePages': _('Like Pages', formatted=False),
             'LocalSiteMap': _('Local Site Map', formatted=False),
--- a/docs/CHANGES	Sat Mar 03 00:17:41 2007 +0100
+++ b/docs/CHANGES	Sat Mar 03 10:47:29 2007 +0100
@@ -334,6 +334,8 @@
       patch.
     * autofilters for databrowser widget. Thanks to Johannes Berg for the patch.
     * action DeletePage and RenamePage could now be used for subpages of a page too
+    * Added Action CopyPage so you can use now an existing page or page hierarchy as template for a new page, see FeatureRequests/CloneOrCopyPages
+      TODO: we need a copy icon in RC
 
   Bugfixes:
     * on action "info" page, "revert" link will not be displayed for empty page