1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - SectionBreakout 4 5 Break sections out of a page, making new pages for each of the sections and 6 replacing them with Include macros. 7 8 @copyright: 2011, 2013 Paul Boddie <paul@boddie.org.uk> 9 @license: GNU GPL, see COPYING for details. 10 """ 11 12 Dependencies = ['pages'] 13 14 from MoinMoin.action import ActionBase 15 from MoinMoin.PageEditor import PageEditor 16 from MoinMoin.wikiutil import escape 17 from MoinContentSupport import * 18 from MoinSupport import ActionSupport, escattr 19 import re 20 21 # Action class and supporting functions. 22 23 class SectionBreakout(ActionBase, ActionSupport): 24 25 "An action breaking sections out of pages." 26 27 def get_form_html(self, buttons_html): 28 _ = self._ 29 request = self.request 30 page = self.page 31 form = self.get_form() 32 33 level = int(form.get("level", ["2"])[0]) 34 35 # Acquire heading details from the page. 36 37 body = page.get_raw_body() 38 heading_details = getSectionDetails(body, level, level) 39 40 d = { 41 "buttons_html" : buttons_html, 42 "heading_level_label" : escape(_("Heading level")), 43 "found_headings_label" : escape(_("Headings found in page")), 44 "propagate_categories_label" : escape(_("Propagate categories")), 45 "preview_label" : escape(_("Preview")), 46 "level" : escattr(level), 47 } 48 49 html = u''' 50 <table> 51 <tr> 52 <td class="label">%(heading_level_label)s</td> 53 <td><input type="text" name="level" value="%(level)s" size="2" /></td> 54 </tr> 55 <tr> 56 <td class="label">%(found_headings_label)s</td> 57 <td>''' % d 58 59 for heading, level, span in heading_details: 60 if heading is not None: 61 html += "%s<br />" % escape(heading) 62 63 html += ''' 64 </td> 65 </tr> 66 <tr> 67 <td class="label">%(propagate_categories_label)s</td> 68 <td class="buttons"><input type="checkbox" name="propagate" value="yes" /></td> 69 </tr> 70 <tr> 71 <td></td> 72 <td class="buttons"><input type="submit" value="%(preview_label)s" />%(buttons_html)s</td> 73 </tr> 74 </table> 75 ''' % d 76 77 return html 78 79 def do_action(self): 80 81 "Create the new event." 82 83 _ = self._ 84 form = self.get_form() 85 86 # A heading level must be provided. 87 88 level = form.get("level", [None])[0] 89 propagate = form.get("propagate", [None])[0] 90 91 if not level: 92 return 0, _("No heading level specified.") 93 94 return self.break_out_headings(int(level), propagate) 95 96 def render_success(self, msg, msgtype=None): 97 98 """ 99 Render neither 'msg' nor 'msgtype' since redirection should occur 100 instead. 101 NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. 102 """ 103 104 pass 105 106 def break_out_headings(self, level, propagate): 107 108 """ 109 Break out headings at the given 'level' from the current page. If 110 'propagate' is a true value, propagate categories to subpages. 111 """ 112 113 _ = self._ 114 request = self.request 115 page = self.page 116 formatter = request.formatter 117 118 # Acquire all heading details from the page. 119 120 page_body = page.get_raw_body() 121 122 if propagate: 123 categories = getCategoryMembership(page_body) 124 125 regions = [] 126 current_region_start = None 127 128 for heading, found_level, (start, end) in getSectionDetails(page_body): 129 130 # Where a heading is provided, consider starting a section. 131 132 if found_level is not None: 133 134 # Upon finding a suitable heading, begin a new region to be broken 135 # out. 136 137 if current_region_start is None and found_level >= level: 138 current_region_start = heading, start 139 140 # Upon finding a same-level or higher-level heading, end any open 141 # region. 142 143 elif current_region_start is not None and found_level <= level: 144 regions.append(current_region_start + (start,)) 145 146 # For headings at the requested level, open a new region. 147 148 if found_level == level: 149 current_region_start = heading, start 150 else: 151 current_region_start = None 152 153 # Where no heading is provided, end the section. 154 155 elif current_region_start is not None: 156 regions.append(current_region_start + (start,)) 157 current_region_start = None 158 159 # End any open region. 160 161 else: 162 if current_region_start is not None: 163 heading, region_start = current_region_start 164 165 # Prevent any capture of end-of-page category information. 166 167 l = getCategoryDeclarations(page_body) 168 if not l: 169 region_end = len(page_body) 170 else: 171 declaration, (start, end) = l[-1] 172 region_end = max(region_start, start) 173 174 regions.append((heading, region_start, region_end)) 175 176 # Make new pages for each region, rebuilding the current page body. 177 178 retained_regions = [] 179 retained_region_start = 0 180 new_page_names = {} 181 182 for heading, start, end in regions: 183 184 # Combine the page name and the heading to make a subpage. 185 186 new_page_name = "%s/%s" % (page.page_name, heading) 187 188 # Distinguish between pages which use the same heading. 189 190 n = new_page_names.get(new_page_name, 0) 191 if n: 192 new_page_names[new_page_name] = n + 1 193 new_page_name += " (%d)" % (n + 1) 194 else: 195 new_page_names[new_page_name] = n + 1 196 197 # Open the page for editing. 198 199 new_page = PageEditor(request, new_page_name) 200 new_page_body = page_body[start:end] 201 202 if propagate: 203 new_page_categories = getCategoryMembership(new_page_body) 204 205 # Add categories if the parent page has any. 206 207 if new_page_categories != categories: 208 new_page_body += makeCategoryDeclaration(categories) 209 210 # Save the new page. 211 212 try: 213 new_page.saveText(new_page_body, 0) 214 except PageEditor.Unchanged: 215 pass 216 217 # Retain the preceding region for the current page. 218 219 retained_regions.append(page_body[retained_region_start:start]) 220 221 # Insert Include macros for the broken out text. 222 223 retained_regions.append("<<Include(%s,,editlink)>>\n" % new_page_name) 224 225 # Start a new region to retain. 226 227 retained_region_start = end 228 229 # Retain any remaining text. 230 231 else: 232 retained_regions.append(page_body[retained_region_start:]) 233 234 # Edit the current page. 235 236 edited_page = PageEditor(request, page.page_name) 237 edited_page_body = "".join(retained_regions) 238 239 if propagate: 240 edited_page_categories = getCategoryMembership(edited_page_body) 241 242 # Add categories if the parent page should have any, but these were 243 # broken out. 244 245 if edited_page_categories != categories: 246 edited_page_body += makeCategoryDeclaration(categories) 247 248 # Save the current page. 249 250 edited_page.saveText(edited_page_body, 0) 251 252 # NOTE: Perhaps show a message upon failure. 253 254 request.http_redirect(page.url(request)) 255 return 1, None 256 257 # Action function. 258 259 def execute(pagename, request): 260 SectionBreakout(pagename, request).render() 261 262 # vim: tabstop=4 expandtab shiftwidth=4