def execute(xmlrpcobj, pagename, filename=None, action='save', content=None, overwrite=False): request = xmlrpcobj.request _ = request.getText pagename = xmlrpcobj._instr(pagename) pagename = normalize_pagename(pagename, request.cfg) # Fault at empty pagenames if not pagename: return xmlrpclib.Fault(3, _("No page name entered")) # It is possible just to attach data to the cache if not filename: filename = cache_key(request, (pagename, content)) else: filename = xmlrpcobj._instr(filename) if action == 'list': success = list(request, pagename) elif action == 'load': success = load(request, pagename, filename) elif action == 'delete': success = delete(request, pagename, filename) elif action == 'save' and content: success = save(request, pagename, filename, content.data, overwrite) else: success = xmlrpclib.Fault(3, _("No method specified or empty data")) # Save, delete return the file name on success if success is True: return xmlrpclib.Binary(filename) # Other results include the faults and the binary pagecachefiles return success
def do_action(self): """ copy this page to "pagename" """ _ = self._ # Currently we only check TextCha for upload (this is what spammers ususally do), # but it could be extended to more/all attachment write access if not TextCha(self.request).check_answer_from_form(): return status, _('TextCha: Wrong answer! Go back and try again...') form = self.form newpagename = form.get('newpagename', u'') newpagename = wikiutil.normalize_pagename(newpagename, self.cfg) comment = form.get('comment', u'') comment = wikiutil.clean_input(comment) self.page = PageEditor(self.request, self.pagename) success, msgs = self.page.copyPage(newpagename, comment) copy_subpages = 0 try: copy_subpages = int(form['copy_subpages']) 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) msgs = "%s %s" % (msgs, msg) self.newpagename = newpagename # keep there for finish return success, msgs
def xmlrpc_renamePage(self, pagename, newpagename): """ Renames a page <pagename> to <newpagename>. @param pagename: the page name (unicode or utf-8) @param newpagename: the new pagename (unicode or utf-8) @rtype: bool @return: True on success """ pagename = self._instr(pagename) pagename = wikiutil.normalize_pagename(pagename, self.cfg) if not pagename: return xmlrpclib.Fault("INVALID", "pagename can't be empty") # check ACLs if not (self.request.user.may.delete(pagename) and self.request.user.may.write(newpagename)): return xmlrpclib.Fault(1, "You are not allowed to rename this page") editor = PageEditor(self.request, pagename, do_editor_backup=0) try: editor.renamePage(newpagename) except PageEditor.SaveError, error: return xmlrpclib.Fault(1, "Rename failed: %s" % (str(error), ))
def execute(xmlrpcobj, pagename, filename, action='save', content=None, overwrite=False, log=True): request = xmlrpcobj.request _ = request.getText pagename = xmlrpcobj._instr(pagename) filename = xmlrpcobj._instr(filename) pagename = normalize_pagename(pagename, request.cfg) # Fault at empty pagenames if not pagename: return xmlrpclib.Fault(3, _("No page name entered")) if action == 'list': success = list(request, pagename) elif action == 'load': success = load(request, pagename, filename) elif action == 'delete': success = delete(request, pagename, filename, log) elif action == 'save' and content: success = save(request, pagename, filename, content.data, overwrite, log) else: success = xmlrpclib.Fault(3, _("No method specified or empty data")) # Save, delete return True on success if success is True: return xmlrpclib.Boolean(1) # Other results include the faults and the binary attachments return success
def xmlrpc_putPage(self, pagename, pagetext): """ save a page / change a page to a new text @param pagename: the page name (unicode or utf-8) @param pagetext: the new page text (content, unicode or utf-8) @rtype: bool @return: True on success """ pagename = self._instr(pagename) pagename = wikiutil.normalize_pagename(pagename, self.cfg) if not pagename: return xmlrpclib.Fault("INVALID", "pagename can't be empty") # check ACLs if not self.request.user.may.write(pagename): return xmlrpclib.Fault(1, "You are not allowed to edit this page") page = PageEditor(self.request, pagename, do_editor_backup=0) try: if self.version == 2: newtext = self._instr(pagetext) elif self.version == 1: newtext = self._inlob(pagetext) msg = page.saveText(newtext, 0) except page.SaveError, msg: logging.error("SaveError: %s" % msg) return xmlrpclib.Fault(1, "%s" % msg)
def dispatch(request, context, action_name='show'): cfg = context.cfg # The last component in path_info is the page name, if any path = remove_prefix(request.path, cfg.url_prefix_action) if path.startswith('/'): pagename = wikiutil.normalize_pagename(path, cfg) else: pagename = None # need to inform caches that content changes based on: # * cookie (even if we aren't sending one now) # * User-Agent (because a bot might be denied and get no content) # * Accept-Language (except if moin is told to ignore browser language) hs = HeaderSet(('Cookie', 'User-Agent')) if not cfg.language_ignore_browser: hs.add('Accept-Language') request.headers['Vary'] = str(hs) # Handle request. We have these options: # 1. jump to page where user left off if not pagename and context.user.remember_last_visit and action_name == 'show': response = redirect_last_visited(context) # 2. handle action else: response = handle_action(context, pagename, action_name) if isinstance(response, Context): response = response.request return response
def execute(pagename, request): request.headers["Content-Type"] = "text/plain; charset=ascii" if request.environ['REQUEST_METHOD'] != 'POST': return form = values_to_form(request.values) content = form.get('content', [None])[0] if not content: sendfault(request, "Missing page content") return pagename = normalize_pagename(pagename, request.cfg) if not pagename: sendfault(request, "No page name entered") return if not request.user.may.write(pagename): sendfault(request, "You are not allowed to edit this page") return page = PageEditor(request, pagename) if page.exists(): sendfault(request, "Page already exists.") return msg = "" try: msg = page.saveText(content, 0) except page.SaveError, msg: sendfault(request, "Failed to save page: %s" % pagename) return
def pagelink(self, on, pagename='', page=None, **kw): if self._catch_name: if not pagename and page: pagename = page.page_name name = wikiutil.normalize_pagename(pagename, self.request.cfg) self.members.append(name) self._catch_name = False return self.null()
def collectpackage(self, pagelist, fileobject, pkgname="", include_attachments=False): """ Expects a list of pages as an argument, and fileobject to be an open file object, which a zipfile will get written to. @param pagelist: pages to package @param fileobject: open file object to write to @param pkgname: optional file name, to prevent self packaging @rtype: string or None @return: error message, if one happened @rtype: boolean @param include_attachments: True if you want attachments collected """ _ = self.request.getText COMPRESSION_LEVEL = zipfile.ZIP_DEFLATED pages = [] for pagename in pagelist: pagename = wikiutil.normalize_pagename(pagename, self.request.cfg) if pagename: page = Page(self.request, pagename) if page.exists() and self.request.user.may.read(pagename): pages.append(page) if not pages: return (_('No pages like "%s"!') % wikiutil.escape(pagelist)) # Set zipfile output zf = zipfile.ZipFile(fileobject, "w", COMPRESSION_LEVEL) cnt = 0 userid = user.getUserIdentification(self.request) script = [packLine(['MoinMoinPackage', '1']), ] for page in pages: cnt += 1 files = _get_files(self.request, page.page_name) script.append(packLine(["AddRevision", str(cnt), page.page_name, userid, "Created by the PackagePages action."])) timestamp = wikiutil.version2timestamp(page.mtime_usecs()) # avoid getting strange exceptions from zipfile in case of pre-1980 timestamps nineteeneighty = (10 * 365 + 3) * 24 * 3600 # 1970 + 10y + 3d timestamp = max(nineteeneighty, timestamp) # zip can not store timestamps before 1980 zi = zipfile.ZipInfo(filename=str(cnt), date_time=datetime.fromtimestamp(timestamp).timetuple()[:6]) zi.compress_type = COMPRESSION_LEVEL zf.writestr(zi, page.get_raw_body().encode("utf-8")) if include_attachments: for attname in files: if attname != pkgname: cnt += 1 zipname = "%d_attachment" % cnt script.append(packLine(["AddAttachment", zipname, attname, page.page_name, userid, "Created by the PackagePages action."])) filename = AttachFile.getFilename(self.request, page.page_name, attname) zf.write(filename, zipname) script += [packLine(['Print', 'Thank you for using PackagePages!'])] zf.writestr(MOIN_PACKAGE_FILE, u"\n".join(script).encode("utf-8")) zf.close()
def testPageInvalidChars(self): """ request: normalize pagename: remove invalid unicode chars Assume the default setting """ test = u'\u0000\u202a\u202b\u202c\u202d\u202e' expected = u'' result = wikiutil.normalize_pagename(test, self.request.cfg) assert result == expected
def pagelink(self, on, pagename='', page=None, **kw): if self._bullet_list_level == 1: self._inside_link = on if not on: if not pagename and page: pagename = page.page_name pagename = wikiutil.normalize_pagename(pagename, self.request.cfg) self._new_member += pagename return self.null()
def pagelink(self, on, pagename='', page=None, **kw): """ make a link to page <pagename>. Instead of supplying a pagename, it is also possible to give a live Page object, then page.page_name will be used. """ if not self._store_pagelinks or not on or kw.get('generated'): return '' if not pagename and page: pagename = page.page_name pagename = wikiutil.normalize_pagename(pagename, self.request.cfg) if pagename and pagename not in self.pagelinks: self.pagelinks.append(pagename)
def do_action(self): _ = self.request.getText pdata = self.request.graphdata.getpage(self.pagename) oldpagename = self.pagename success, msgs = RenamePageBasic.do_action(self) form = values_to_form(self.request.values) rename_links = 0 if 'rename_links' in form: try: rename_links = int(form['rename_links'][0]) except: pass if rename_links and success: newpagename = form.get('newpagename', [u''])[0] newpagename = wikiutil.normalize_pagename(newpagename, self.cfg) comment = form.get('comment', [u''])[0] comment = wikiutil.clean_input(comment) comment = "%s (%s)" % (comment, _("changed links:") + " %s -> %s" % (self.pagename, newpagename)) # List pages that link to the renamed page pages = set() inlinks = self.request.graphdata.get_in(self.pagename) for type in inlinks: pages.update(inlinks[type]) # Update listed pages for page in pages: # User rights _ARE_ checked here! if not self.request.user.may.write(page): continue # If inlink rename of a single page does not work, # continue but make sure to emit a warning success_single, msg = self._inlink_rename(page, newpagename, oldpagename, comment) if not success_single: success = False if msg: msgs = self._add_msg(msgs, msg) if not success: msgs = self._add_msg(msgs, _(u'Other pages with inlinks renamed successfully.')) return success, msgs
def testNormalizeSlashes(self): """ request: normalize pagename: normalize slashes """ cases = ( (u'/////', u''), (u'/a', u'a'), (u'a/', u'a'), (u'a/////b/////c', u'a/b/c'), (u'a b/////c d/////e f', u'a b/c d/e f'), ) for test, expected in cases: result = wikiutil.normalize_pagename(test, self.request.cfg) assert result == expected
def testNormalizeWhitespace(self): """ request: normalize pagename: normalize whitespace """ cases = ( (u' ', u''), (u' a', u'a'), (u'a ', u'a'), (u'a b c', u'a b c'), (u'a b / c d / e f', u'a b/c d/e f'), # All 30 unicode spaces (config.chars_spaces, u''), ) for test, expected in cases: result = wikiutil.normalize_pagename(test, self.request.cfg) assert result == expected
def testNormalizeWhitespace(self): """ request: normalize pagename: normalize whitespace """ cases = ( (u' ', u''), (u' a', u'a'), (u'a ', u'a'), (u'a b c', u'a b c'), (u'a b / c d / e f', u'a b/c d/e f'), # All 30 unicode spaces (CHARS_SPACES, u''), ) for test, expected in cases: result = wikiutil.normalize_pagename(test, app.cfg) assert result == expected
def testNormalizeGroupName(self): """ request: normalize pagename: restrict groups to alpha numeric Unicode Spaces should normalize after invalid chars removed! """ cases = ( # current acl chars (u'Name,:Group', u'NameGroup'), # remove than normalize spaces (u'Name ! @ # $ % ^ & * ( ) + Group', u'Name Group'), ) for test, expected in cases: # validate we are testing valid group names if wikiutil.isGroupPage(test, self.request.cfg): result = wikiutil.normalize_pagename(test, self.request.cfg) assert result == expected
def testUnderscoreTestCase(self): """ request: normalize pagename: underscore convert to spaces and normalized Underscores should convert to spaces, then spaces should be normalized, order is important! """ cases = ( (u' ', u''), (u' a', u'a'), (u'a ', u'a'), (u'a b c', u'a b c'), (u'a b / c d / e f', u'a b/c d/e f'), ) for test, expected in cases: result = wikiutil.normalize_pagename(test, self.request.cfg) assert result == expected
def check_grouppage(request, grouppage, writecheck=True, createcheck=True): _ = request.getText grouppage = normalize_pagename(grouppage, request.cfg) if not isGroupPage(grouppage, request.cfg): return False, _("Invalid group name.") if writecheck: if not request.user.may.write(grouppage): return False, _("You are not allowed to edit this page.") if createcheck: try: if not isinstance(request.groups[grouppage], WikiGroup): return False, _("Invalid group.") except GroupDoesNotExistError: return False, _("Invalid group.") return True, ''
def do_action(self): """ Rename this page to "pagename" """ _ = self._ form = self.form newpagename = form.get('newpagename', u'') newpagename = wikiutil.normalize_pagename(newpagename, self.cfg) comment = form.get('comment', u'') comment = wikiutil.clean_input(comment) try: rename_subpages = int(self.request.form.get('rename_subpages', '0')) except ValueError: rename_subpages = 0 self.page = PageEditor(self.request, self.pagename) success, msgs = self.page.renamePage(newpagename, comment) if not success: return success, msgs msgs = [msgs] if rename_inlinks: success, msgs = self.rename_inlinks(newpagename, comment, msgs) if self.show_redirect and self.rename_redirect: self.page = PageEditor(self.request, self.pagename) self.page.saveText('#redirect %s' % newpagename, 0) if rename_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.renamePage(new_subpagename, comment) msgs.append(msg) if self.show_redirect and self.rename_redirect and success_i: self.page = PageEditor(self.request, name) self.page.saveText('#redirect %s' % new_subpagename, 0) msgs = ' '.join([msg for msg in msgs if msg]) self.newpagename = newpagename # keep there for finish return success, msgs
def execute(pagename, request): _ = request.getText request.content_type = "application/json" if request.environ['REQUEST_METHOD'] != 'POST': return # normalization is performed in set_metas, this check is retained # to provide early feedback to the user. pagename = wikiutil.normalize_pagename(pagename, request.cfg) if not pagename: sendfault(request, 2, _("No page name entered")) return form = values_to_form(request.values) indata = form.get('args', [None])[0] if not indata: return indata = json.loads(indata) if 'metas' not in indata: msg = [] for xpagename, metadict in indata.items(): r = doit(request, xpagename, {'metas': metadict, 'action': 'set'}) if not r: continue if type(r) is list: msg += r else: msg.append(r) else: msg = doit(request, pagename, indata) if msg: json.dump(dict(status="ok", msg=msg), request) return
def execute(xmlrpcobj, page, fname, action='info', start=None, end=None): request = xmlrpcobj.request _ = request.getText page = xmlrpcobj._instr(page) fname = xmlrpcobj._instr(fname) page = normalize_pagename(page, request.cfg) # Fault at empty pagenames if not page: return xmlrpclib.Fault(3, _("No page name entered")) if action == 'info': success = runChecked(info, request, page, fname) elif action == 'load' and None not in (start, end): success = runChecked(load, request, page, fname, start, end) elif action == 'reassembly' and start is not None: chunk, digests = start, end success = runChecked(reassembly, request, page, fname, chunk, digests) else: success = xmlrpclib.Fault(3, _("No method specified or invalid span")) return success
def execute(xmlrpcobj, pagename, comment = None): request = xmlrpcobj.request _ = request.getText pagename = xmlrpcobj._instr(pagename) pagename = normalize_pagename(pagename, request.cfg) # Fault at empty pagenames if not pagename: return xmlrpclib.Fault(2, _("No page name entered")) if comment: comment = xmlrpcobj._instr(comment) else: comment = u'' success = delete(request, pagename, comment) # Save, delete return True on success if success is True: return xmlrpclib.Boolean(1) # Other results include the faults and the binary attachments return success
def set_metas(request, cleared, discarded, added): pages = set(cleared) | set(discarded) | set(added) # Discard empties and junk pages = [wikiutil.normalize_pagename(x, request.cfg) for x in pages] pages = [x for x in pages if x] msg = list() # We don't have to check whether the user is allowed to read # the page, as we don't send any info on the pages out. Only # check that we can write to the requested pages. for page in pages: if not request.user.may.write(page): message = "You are not allowed to edit page '%s'" % page return False, request.getText(message) for page in pages: pageCleared = cleared.get(page, set()) pageDiscarded = discarded.get(page, dict()) pageAdded = added.get(page, dict()) # Template clears might make sense at some point, not implemented if TEMPLATE_KEY in pageCleared: pageCleared.remove(TEMPLATE_KEY) # Template changes might make sense at some point, not implemented if TEMPLATE_KEY in pageDiscarded: del pageDiscarded[TEMPLATE_KEY] # Save templates for empty pages if TEMPLATE_KEY in pageAdded: save_template(request, page, ''.join(pageAdded[TEMPLATE_KEY])) del pageAdded[TEMPLATE_KEY] metakeys = set(pageCleared) | set(pageDiscarded) | set(pageAdded) # Filter out uneditables, such as inlinks metakeys = editable_p(metakeys) old = get_metas(request, page, metakeys, checkAccess=False, includeGenerated=False) new = dict() for key in old: values = old.pop(key) old[key] = values new[key] = set(values) for key in pageCleared: new[key] = set() for key, values in pageDiscarded.iteritems(): for v in values: new[key].difference_update(values) for key, values in pageAdded.iteritems(): new[key].update(values) for key, values in new.iteritems(): ordered = copy.copy(old[key]) for index, value in enumerate(ordered): if value not in values: ordered[index] = u"" values.difference_update(ordered) ordered.extend(values) new[key] = ordered msg.append(edit_meta(request, page, old, new)) return True, msg
def split_navilink(self, text, localize=1): """ Split navibar links into pagename, link to page Admin or user might want to use shorter navibar items by using the [[page|title]] or [[url|title]] syntax. In this case, we don't use localization, and the links goes to page or to the url, not the localized version of page. Supported syntax: * PageName * WikiName:PageName * wiki:WikiName:PageName * url * all targets as seen above with title: [[target|title]] @param text: the text used in config or user preferences @rtype: tuple @return: pagename or url, link to page or url """ title = None wiki_local = '' # means local wiki # Handle [[pagename|title]] or [[url|title]] formats if text.startswith('[[') and text.endswith(']]'): text = text[2:-2] try: target, title = text.split('|', 1) target = target.strip() title = title.strip() localize = 0 except (ValueError, TypeError): # Just use the text as is. target = text.strip() else: target = text if wikiutil.is_URL(target): if not title: title = target return target, title, wiki_local # remove wiki: url prefix if target.startswith("wiki:"): target = target[5:] # try handling interwiki links wiki_name, item_name = split_interwiki(target) wiki_name, wiki_base_url, item_name, err = resolve_interwiki(wiki_name, item_name) href = join_wiki(wiki_base_url, item_name) if wiki_name not in [self.cfg.interwikiname, 'Self', ]: if not title: title = item_name return href, title, wiki_name # Handle regular pagename like "FrontPage" item_name = wikiutil.normalize_pagename(item_name, self.cfg) # Use localized pages for the current user if localize: item_name = self.translated_item_name(item_name) if not title: title = item_name href = url_for('frontend.show_item', item_name=item_name) return href, title, wiki_local
def get_pagename_content(request, msg): """ Generates pagename and content according to the specification that can be found on MoinMoin:FeatureRequests/WikiEmailintegration """ generate_summary = False choose_html = True cfg = request.cfg email_subpage_template = cfg.mail_import_subpage_template email_pagename_envelope = cfg.mail_import_pagename_envelope wiki_addrs = cfg.mail_import_wiki_addrs search_list = cfg.mail_import_pagename_search re_subject = re.compile(cfg.mail_import_pagename_regex) subj = msg['subject'].strip() pagename_tpl = "" for method in search_list: if method == 'to': for addr in msg['target_addrs']: if addr[1].strip().lower() in wiki_addrs: pagename_tpl = addr[0] # special fix for outlook users :-) if pagename_tpl and pagename_tpl[-1] == pagename_tpl[ 0] == "'": pagename_tpl = pagename_tpl[1:-1] if pagename_tpl: break elif method == 'subject': m = re_subject.search(subj) if m: pagename_tpl = m.group(1) # remove the pagename template from the subject: subj = re_subject.sub('', subj, 1).strip() if pagename_tpl: break pagename_tpl = pagename_tpl.strip() # last resort if not pagename_tpl: pagename_tpl = email_subpage_template if not subj: subj = '(...)' # we need non-empty subject msg['subject'] = subj # for normal use, email_pagename_envelope is just u"%s" - so nothing changes. # for special use, you can use u"+ %s/" - so you don't need to enter "+" # and "/" in every email, but you get the result as if you did. pagename_tpl = email_pagename_envelope % pagename_tpl if pagename_tpl.endswith("/"): pagename_tpl += email_subpage_template subject = msg['subject'].replace('/', '\\') # we can't use / in pagenames # rewrite using string.formatter when python 2.4 is mandatory pagename = (pagename_tpl.replace("$from", msg['from_addr'][0]).replace( "$date", msg['date']).replace("$subject", subject)) if pagename.startswith("+ ") and "/" in pagename: generate_summary = True pagename = pagename[1:].lstrip() pagename = wikiutil.normalize_pagename(pagename, request.cfg) if choose_html and msg['html']: content = "{{{#!html\n%s\n}}}" % msg['html'].replace("}}}", "} } }") else: # strip signatures ... content = re_sigstrip.sub("", msg['text']) return { 'pagename': pagename, 'content': content, 'generate_summary': generate_summary }
def get_pagename_content(request, msg): """ Generates pagename and content according to the specification that can be found on MoinMoin:FeatureRequests/WikiEmailintegration """ generate_summary = False choose_html = True cfg = request.cfg email_subpage_template = cfg.mail_import_subpage_template email_pagename_envelope = cfg.mail_import_pagename_envelope wiki_addrs = cfg.mail_import_wiki_addrs search_list = cfg.mail_import_pagename_search re_subject = re.compile(cfg.mail_import_pagename_regex) subj = msg['subject'].strip() pagename_tpl = "" for method in search_list: if method == 'to': for addr in msg['target_addrs']: if addr[1].strip().lower() in wiki_addrs: pagename_tpl = addr[0] # special fix for outlook users :-) if pagename_tpl and pagename_tpl[-1] == pagename_tpl[0] == "'": pagename_tpl = pagename_tpl[1:-1] if pagename_tpl: break elif method == 'subject': m = re_subject.search(subj) if m: pagename_tpl = m.group(1) # remove the pagename template from the subject: subj = re_subject.sub('', subj, 1).strip() if pagename_tpl: break pagename_tpl = pagename_tpl.strip() # last resort if not pagename_tpl: pagename_tpl = email_subpage_template if not subj: subj = '(...)' # we need non-empty subject msg['subject'] = subj # for normal use, email_pagename_envelope is just u"%s" - so nothing changes. # for special use, you can use u"+ %s/" - so you don't need to enter "+" # and "/" in every email, but you get the result as if you did. pagename_tpl = email_pagename_envelope % pagename_tpl if pagename_tpl.endswith("/"): pagename_tpl += email_subpage_template subject = msg['subject'].replace('/', '\\') # we can't use / in pagenames # rewrite using string.formatter when python 2.4 is mandatory pagename = (pagename_tpl.replace("$from", msg['from_addr'][0]). replace("$date", msg['date']). replace("$subject", subject)) if pagename.startswith("+ ") and "/" in pagename: generate_summary = True pagename = pagename[1:].lstrip() pagename = wikiutil.normalize_pagename(pagename, request.cfg) if choose_html and msg['html']: content = "{{{#!html\n%s\n}}}" % msg['html'].replace("}}}", "} } }") else: # strip signatures ... content = re_sigstrip.sub("", msg['text']) return {'pagename': pagename, 'content': content, 'generate_summary': generate_summary}