ApproveChanges

Changeset

11:5c1da117cde3
2011-10-13 Paul Boddie raw files shortlog changelog graph Added support for merging competing edits, opening the editor on a page where already approved changes conflict with competing queued changes. Changed the directives removal function to return directive values so that the parent revision can be obtained and used to retrieve the target page's body for that revision.
ApproveChangesSupport.py (file) actions/ApproveChanges.py (file)
     1.1 --- a/ApproveChangesSupport.py	Thu Oct 13 22:48:25 2011 +0200
     1.2 +++ b/ApproveChangesSupport.py	Thu Oct 13 23:31:45 2011 +0200
     1.3 @@ -18,14 +18,12 @@
     1.4  from MoinMoin import user
     1.5  from MoinMoin.Page import Page
     1.6  from MoinMoin.wikiutil import escape
     1.7 -
     1.8 -try:
     1.9 -    set
    1.10 -except NameError:
    1.11 -    from sets import Set as set
    1.12 +import re
    1.13  
    1.14  __version__ = "0.1"
    1.15  
    1.16 +space_pattern = re.compile("(\s+)")
    1.17 +
    1.18  def get_queued_changes_page(request):
    1.19      return getattr(request.cfg, "queued_changes_page", "ApprovalQueue")
    1.20  
    1.21 @@ -112,13 +110,14 @@
    1.22  def remove_directives(body, names):
    1.23  
    1.24      """
    1.25 -    Remove from the page 'body' the first of each directive using the given
    1.26 -    'names'.
    1.27 +    Return a new page body, copying the page 'body' provided but removing the
    1.28 +    first of each directive having one of the given 'names', along with a
    1.29 +    dictionary mapping directive names to values.
    1.30      """
    1.31  
    1.32      new_body = []
    1.33      header = 1
    1.34 -    found = set()
    1.35 +    found = {}
    1.36  
    1.37      for line in body.split("\n"):
    1.38          if header:
    1.39 @@ -127,20 +126,23 @@
    1.40  
    1.41              if not line.startswith("#"):
    1.42                  header = 0
    1.43 +
    1.44 +            # Process the comment or directive.
    1.45 +
    1.46              else:
    1.47 -                parts = line[1:].split()
    1.48 +                parts = space_pattern.split(line[1:])
    1.49  
    1.50                  # Identify any directive.
    1.51  
    1.52                  directive = parts[0]
    1.53  
    1.54 -                if directive in names and directive not in found:
    1.55 -                    found.add(directive)
    1.56 +                if directive in names and not found.has_key(directive):
    1.57 +                    found[directive] = "".join(parts[1:])
    1.58                      continue
    1.59  
    1.60          new_body.append(line)
    1.61  
    1.62 -    return "\n".join(new_body)
    1.63 +    return "\n".join(new_body), found
    1.64  
    1.65  # Utility classes and associated functions.
    1.66  # NOTE: These are a subset of EventAggregatorSupport.
     2.1 --- a/actions/ApproveChanges.py	Thu Oct 13 22:48:25 2011 +0200
     2.2 +++ b/actions/ApproveChanges.py	Thu Oct 13 23:31:45 2011 +0200
     2.3 @@ -15,7 +15,8 @@
     2.4  
     2.5  from MoinMoin.action import ActionBase
     2.6  from MoinMoin.Page import Page
     2.7 -from MoinMoin.PageEditor import PageEditor
     2.8 +from MoinMoin.PageEditor import PageEditor, conflict_markers
     2.9 +from MoinMoin.util import diff3
    2.10  from ApproveChangesSupport import *
    2.11  
    2.12  # Action class and supporting functions.
    2.13 @@ -82,10 +83,8 @@
    2.14          if not is_queued_changes_page(request, self.pagename):
    2.15              return 0, _("This page is not queued for approval.")
    2.16  
    2.17 -        target_page_name = get_target_page_name(self.pagename)
    2.18 -        target_page = PageEditor(request, target_page_name)
    2.19 -
    2.20 -        # First, the displayed revision must be retrieved.
    2.21 +        # First, the displayed revision of the queued changes page must be
    2.22 +        # retrieved.
    2.23  
    2.24          form = get_form(request)
    2.25  
    2.26 @@ -100,16 +99,51 @@
    2.27          page = Page(request, self.page.page_name, rev=rev)
    2.28          body = page.get_raw_body()
    2.29  
    2.30 -        # Save the target page, removing any introduced directives.
    2.31 +        # Remove any introduced directives.
    2.32 +
    2.33 +        body, directives = remove_directives(body, ["acl", "parent-revision"])
    2.34 +
    2.35 +        # Get the target page's parent revision for the queued changes.
    2.36 +
    2.37 +        target_page_name = get_target_page_name(self.pagename)
    2.38 +        target_page = PageEditor(request, target_page_name)
    2.39 +
    2.40 +        current_rev = target_page.current_rev()
    2.41 +        parent_rev = int(directives.get("parent-revision", current_rev))
    2.42 +
    2.43 +        # Where the parent revision differs from the current revision of the
    2.44 +        # page, attempt to merge the changes.
    2.45 +
    2.46 +        query = {}
    2.47 +
    2.48 +        if parent_rev != current_rev:
    2.49  
    2.50 -        body = remove_directives(body, ["acl", "parent-revision"])
    2.51 +            # The body of the parent revision of the target page, along with the
    2.52 +            # body of the current revision must be acquired.
    2.53 +
    2.54 +            parent_body = Page(request, target_page_name, rev=parent_rev).get_raw_body()
    2.55 +            current_body = target_page.get_raw_body()
    2.56 +
    2.57 +            # The parent, current and queued texts must then be merged.
    2.58 +
    2.59 +            body = diff3.text_merge(parent_body, current_body, body, True, *conflict_markers)
    2.60 +
    2.61 +            # Look for conflict markers and redirect to edit mode on the
    2.62 +            # resulting page if they are present.
    2.63 +
    2.64 +            for marker in conflict_markers:
    2.65 +                if body.find(marker) != -1:
    2.66 +                    query = {'action' : 'edit'}
    2.67 +                    break
    2.68 +
    2.69 +        # Save the target page.
    2.70  
    2.71          try:
    2.72              target_page.saveText(body, 0, comment=_("Changes to page approved from queue revision %d.") % rev)
    2.73          except PageEditor.Unchanged:
    2.74              pass
    2.75  
    2.76 -        # Delete the current page.
    2.77 +        # Delete the queued changes page.
    2.78          # NOTE: The page could be deleted completely or certain revisions
    2.79          # NOTE: purged.
    2.80          # NOTE: (to-do/proper-queued-page-deletion.txt)
    2.81 @@ -117,9 +151,9 @@
    2.82          current_page = PageEditor(request, self.pagename)
    2.83          current_page.deletePage(_("Changes to page approved."))
    2.84  
    2.85 -        # Redirect to the target page.
    2.86 +        # Redirect to the target page, potentially in editing mode.
    2.87  
    2.88 -        request.http_redirect(target_page.url(request))
    2.89 +        request.http_redirect(target_page.url(request, query))
    2.90          return 1, None
    2.91  
    2.92      def render_success(self, msg, msgtype):