1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - AddLinkToPage 4 5 Add a link using a form in the page, getting details of the linked document 6 and inserting them with the link itself. 7 8 @copyright: 2010 Paul Boddie <paul@boddie.org.uk> 9 @license: GNU GPL, see COPYING for details. 10 """ 11 12 Dependencies = ['pages'] 13 14 from MoinMoin import wikiutil 15 from MoinMoin.action import ActionBase 16 from MoinMoin.PageEditor import PageEditor 17 from MoinContentSupport import ActionSupport 18 import urllib 19 import re 20 21 # Page parsing. 22 23 macro_pattern = re.compile(ur'^(?P<leading>.*?)<<AddLinkToPage\((?P<identifier>[^\s,)]+).*?\)>>(?P<trailing>.*)$', 24 re.MULTILINE | re.UNICODE) 25 26 # Link visiting and parsing. 27 28 title_pattern = re.compile(ur'<(?P<tag>title|h\d)(\s.*?)?>(?P<title>.*?)</(?P=tag)>', re.MULTILINE | re.UNICODE | re.DOTALL) 29 paragraph_pattern = re.compile(ur'<p(\s.*?)?>(?P<text>.*?)(?=<p(\s.*?)?>|</p>)', re.MULTILINE | re.UNICODE | re.DOTALL) 30 tag_pattern = re.compile(ur'<.*?>', re.MULTILINE | re.UNICODE | re.DOTALL) 31 32 def get_link_info(link): 33 34 "Get information from the given 'link'." 35 36 # NOTE: Insist on remote URLs! 37 38 try: 39 f = urllib.urlopen(link) 40 except IOError: 41 return None 42 43 try: 44 s = f.read() 45 first_title = "" 46 47 for title_match in title_pattern.finditer(s): 48 title = title_match.group("title").strip() 49 start, end = title_match.span() 50 51 if not first_title: 52 first_title = title 53 54 for intro_match in paragraph_pattern.finditer(s[end:]): 55 intro = get_flattened_content(intro_match.group("text")).strip() 56 if intro: 57 return title, intro 58 finally: 59 f.close() 60 61 return first_title, "" 62 63 def get_flattened_content(s): 64 65 "Get HTML or XHTML without the tags." 66 67 l = [] 68 last = 0 69 for match in tag_pattern.finditer(s): 70 start, end = match.span() 71 l.append(s[last:start]) 72 last = end 73 l.append(s[last:]) 74 return "".join(l).replace("\n", " ") 75 76 # Action class and supporting functions. 77 78 class AddLinkToPage(ActionBase, ActionSupport): 79 80 "An action adding links to pages." 81 82 def get_form_html(self, buttons_html): 83 _ = self._ 84 request = self.request 85 page = self.page 86 form = self.get_form() 87 88 identifier = form.get("identifier", [None])[0] 89 link = form.get("link", [None])[0] 90 insert_before = form.get('insert_before', [""])[0] 91 title = "" 92 introduction = "" 93 94 # Acquire information from the link. 95 96 if link is not None: 97 link_info = get_link_info(link) 98 99 # NOTE: Perhaps show a message upon success/failure. 100 101 if link_info is not None: 102 title, introduction = link_info 103 104 d = { 105 "identifier" : wikiutil.escape(identifier, 1), 106 "insert_before" : insert_before and "true" or "", 107 "link" : wikiutil.escape(link, 1), 108 "title" : wikiutil.escape(title, 1), 109 "intro" : wikiutil.escape(introduction, 1), 110 "url_label" : wikiutil.escape(_("URL")), 111 "title_label" : wikiutil.escape(_("Title")), 112 "intro_label" : wikiutil.escape(_("Introduction")), 113 "description_label" : wikiutil.escape(_("Description")), 114 "submit_label" : wikiutil.escape(_("Submit link")), 115 "script_name" : request.getScriptname(), 116 "page_url" : wikiutil.quoteWikinameURL(page.page_name) 117 } 118 119 html = ''' 120 <form class="macro" method="POST" action="%(script_name)s/%(page_url)s"> 121 <input type="hidden" name="identifier" value="%(identifier)s" /> 122 <input type="hidden" name="doit" value="1" /> 123 <input type="hidden" name="insert_before" value="%(insert_before)s" /> 124 <input type="hidden" name="action" value="AddLinkToPage" /> 125 <table> 126 <tr> 127 <td class="label">%(url_label)s</td> 128 <td><input type="text" name="link" value="%(link)s" size="40" /></td> 129 </tr> 130 <tr> 131 <td class="label">%(title_label)s</td> 132 <td><input type="text" name="title" value="%(title)s" size="40" /></td> 133 </tr> 134 <tr> 135 <td class="label">%(intro_label)s</td> 136 <td><textarea name="introduction" cols="40" rows="3">%(intro)s</textarea></td> 137 </tr> 138 <tr> 139 <td class="label">%(description_label)s</td> 140 <td><input type="text" name="description" size="40" /></td> 141 </tr> 142 <tr> 143 <td colspan="2"><input type="submit" value="%(submit_label)s" /></td> 144 </tr> 145 </table> 146 </form>''' % d 147 148 return html 149 150 def do_action(self): 151 152 "Create the new event." 153 154 _ = self._ 155 form = self.get_form() 156 157 # If no title exists in the request, an error message is returned. 158 159 identifier = form.get("identifier", [None])[0] 160 link = form.get("link", [None])[0] 161 162 if not identifier: 163 return 0, _("No identifier specified.") 164 165 if not link: 166 return 0, _("No link specified.") 167 168 return self.add_link_to_page(identifier, link) 169 170 def render_success(self, msg, msgtype=None): 171 172 """ 173 Render neither 'msg' nor 'msgtype' since redirection should occur 174 instead. 175 NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. 176 """ 177 178 pass 179 180 def add_link_to_page(self, identifier, link): 181 182 """ 183 For the macro with the given 'identifier', add 'link' to the current 184 page. 185 """ 186 187 _ = self._ 188 request = self.request 189 page = self.page 190 formatter = request.formatter 191 form = self.get_form() 192 193 # Get the link details. 194 195 title = form.get('title', [link])[0] 196 introduction = form.get('introduction', [""])[0] 197 description = form.get('description', [""])[0] 198 insert_before = form.get('insert_before', [""])[0] 199 200 # Encode the link details. 201 # NOTE: Should support different formatting options. 202 203 link_details = "%s[[%s%s]]%s" % ( 204 introduction and (get_verbatim('"%s" ' % introduction)) or "", 205 link, 206 title and ('|%s' % title) or "", 207 description and (" - ''%s''" % description) or "" 208 ) 209 210 # Open the page for editing. 211 212 new_page = PageEditor(request, page.page_name) 213 214 # Parse the page. 215 216 page_body = page.get_raw_body() 217 218 for match in macro_pattern.finditer(page_body): 219 macro_identifier = match.group("identifier") 220 leading_text = match.group("leading") 221 trailing_text = match.group("trailing") 222 start, end = match.span() 223 224 # Where this identifier matches this macro's identifier, insert the 225 # link details. 226 227 if macro_identifier == identifier: 228 if insert_before: 229 page_body = page_body[:start] + leading_text + link_details + trailing_text + "\n" + page_body[start:] 230 else: 231 page_body = page_body[:end] + "\n" + leading_text + link_details + trailing_text + page_body[end:] 232 233 # Save the new version of the page. 234 235 new_page.saveText(page_body, 0) 236 break 237 238 # NOTE: Perhaps show a message upon failure. 239 240 request.http_redirect(page.url(request)) 241 return 1, None 242 243 def get_verbatim(s): 244 245 "Return 's' encoded as verbatim text." 246 247 output = [] 248 249 for part in s.split('"'): 250 if part: 251 output.append('<<Verbatim("%s")>>' % part) 252 else: 253 output.append('') 254 255 return '"'.join(output) 256 257 # Action function. 258 259 def execute(pagename, request): 260 AddLinkToPage(pagename, request).render() 261 262 # vim: tabstop=4 expandtab shiftwidth=4