def test_IncludeHandlesCircularRecursion(self): # detect circular recursion and create error message update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{page2}}') update_item('page2', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{page3}}') update_item('page3', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{page4}}') update_item('page4', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{page2}}') page1 = Item.create('page1') rendered = page1.content._render_data() # an error message will follow strong tag assert '<strong class="moin-error">' in rendered
def build_index(basename, relnames): """ Build a list of IndexEntry by hand, useful as a test helper for index testing. Files have no subitems, meta content is reduced to required keys. """ files = [(IndexEntry( relname, CompositeName(NAMESPACE_DEFAULT, NAME_EXACT, '/'.join( (basename, relname))), Item.create('/'.join((basename, relname))).meta)) for relname in relnames] return [(IndexEntry(f.relname, f.fullname, {key: f.meta[key] for key in (CONTENTTYPE, ITEMTYPE)})) for f in files]
def do_show(self, revid): """ Show a blog item and a list of its blog entries below it. If tag GET-parameter is defined, the list of blog entries consists only of those entries that contain the tag value in their lists of tags. """ # for now it is just one tag=value, later it could be tag=value1&tag=value2&... tag = request.values.get('tag') prefix = self.name + '/' current_timestamp = int(time.time()) terms = [ Term(WIKINAME, app.cfg.interwikiname), # Only blog entry itemtypes Term(ITEMTYPE, ITEMTYPE_BLOG_ENTRY), # Only sub items of this item Prefix(NAME_EXACT, prefix), ] if tag: terms.append(Term(TAGS, tag)) query = And(terms) def ptime_sort_key(searcher, docnum): """ Compute the publication time key for blog entries sorting. If PTIME is not defined, we use MTIME. """ fields = searcher.stored_fields(docnum) ptime = fields.get(PTIME, fields[MTIME]) return ptime ptime_sort_facet = FunctionFacet(ptime_sort_key) revs = flaskg.storage.search(query, sortedby=ptime_sort_facet, reverse=True, limit=None) blog_entry_items = [ Item.create(rev.name, rev_id=rev.revid) for rev in revs ] return render_template( 'blog/main.html', item_name=self.name, fqname=split_fqname(self.name), blog_item=self, blog_entry_items=blog_entry_items, tag=tag, item=self, )
def test_item_can_have_several_names(self): content = u"This is page content" update_item( u'Page', { NAME: [ u'Page', u'Another name', ], CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8' }, content) item1 = Item.create(u'Page') assert item1.name == u'Page' assert item1.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8' assert item1.content.data == content item2 = Item.create(u'Another name') assert item2.name == u'Another name' assert item2.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8' assert item2.content.data == content assert item1.rev.revid == item2.rev.revid
def test_meta_filter(self): name = u'Test_item' contenttype = u'text/plain;charset=utf-8' meta = { 'test_key': 'test_val', CONTENTTYPE: contenttype, NAME: [u'test_name'], ADDRESS: u'1.2.3.4' } item = Item.create(name) result = Item.meta_filter(item, meta) # keys like NAME, ITEMID, REVID, DATAID are filtered expected = {'test_key': 'test_val', CONTENTTYPE: contenttype} assert result == expected
def test_InlineIncludeImage(self): # the 3rd parameter, u'', should be a binary string defining a png image, but it is not needed for this simple test update_item('logo.png', {CONTENTTYPE: 'image/png'}, '') # simple transclusion update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{logo.png}}') rendered = Item.create('page1').content._render_data() assert '<p><span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src=' in rendered assert '/logo.png" /></span></p>' in rendered # simple transclusion with alt text and width update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{logo.png|my alt text|width="100"}}') rendered = Item.create('page1').content._render_data() assert '<p><span class="moin-transclusion" data-href="/logo.png"><img alt="my alt text" src=' in rendered assert 'logo.png" width="100" /></span></p>' in rendered # within paragraph update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, 'text {{logo.png}} text') rendered = Item.create('page1').content._render_data() assert '<p>text <span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src=' in rendered assert '/logo.png" /></span> text</p>' in rendered # within markup update_item( 'page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, "Normal ''italic '''bold {{logo.png}} bold''' italic'' normal") rendered = Item.create('page1').content._render_data() assert '<p>Normal <em>italic <strong>bold <span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src=' in rendered assert '/logo.png" /></span> bold</strong> italic</em> normal</p>' in rendered # multiple transclusions update_item('page1', {CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8'}, '{{logo.png}}{{logo.png}}') rendered = Item.create('page1').content._render_data() assert '<p><span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src=' in rendered assert '/logo.png" /></span><span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src=' in rendered # check for old bug assert '<p />' not in rendered assert '<p></p>' not in rendered
def testIndex(self): # create a toplevel and some sub-items basename = u'Foo' for name in ['', '/ab', '/cd/ef', '/gh', '/ij', '/ij/kl', ]: item = Item.create(basename + name) item._save({CONTENTTYPE: u'text/plain;charset=utf-8'}, "foo") item = Item.create(basename + '/mn') item._save({CONTENTTYPE: u'image/jpeg'}, "JPG") baseitem = Item.create(basename) # test Item.make_flat_index # TODO: test Item.get_subitem_revs dirs, files = baseitem.get_index() assert dirs == build_index(basename, [u'cd', u'ij']) assert files == build_index(basename, [u'ab', u'gh', u'ij', u'mn']) # test Item.get_mixed_index mixed_index = baseitem.get_mixed_index() assert mixed_index == build_mixed_index(basename, [ (u'ab', False), (u'cd', True), (u'gh', False), (u'ij', True), (u'mn', False), ]) # check filtered index when startswith param is passed dirs, files = baseitem.get_index(startswith=u'a') assert dirs == [] assert files == build_index(basename, [u'ab']) # check filtered index when contenttype_groups is passed ctgroups = ["Other Text Items"] dirs, files = baseitem.get_index(selected_groups=ctgroups) assert dirs == build_index(basename, [u'cd', u'ij']) assert files == build_index(basename, [u'ab', u'gh', u'ij'])
def create_comment(meta, message): """ Create a new item comment against original description, refers_to links to original. """ current_timestamp = datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S") item_name = meta[ITEMID] + '/' + 'comment_' + str(current_timestamp) item = Item.create(item_name) item.modify({}, data=message, element='comment', contenttype_guessed='text/x.moin.wiki;charset=utf-8', refers_to=meta[ITEMID], reply_to='', author=flaskg.user.name[0], timestamp=time.ctime())
def file_upload(self, data_file): contenttype = data_file.content_type # guess by browser, based on file name data = data_file.stream check_itemid(self) if self.meta.get(ITEMID) and self.meta.get(NAME): item_name = self.meta[ITEMID] + '/' + data_file.filename refers_to = self.meta[ITEMID] else: item_name = self.fqname.value + '/' + data_file.filename refers_to = self.fqname.value try: item = Item.create(item_name) item.modify({}, data, contenttype_guessed=contenttype, refers_to=refers_to, element=u'file') except AccessDenied: abort(403)
def test_rename_acts_only_in_active_name_in_case_there_are_several_names( self): content = u"This is page content" update_item( u'Page', { NAME: [ u'First', u'Second', u'Third', ], CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8' }, content) item = Item.create(u'Second') item.rename(u'New name', comment=u'renamed') item1 = Item.create(u'First') assert item1.name == u'First' assert item1.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8' assert item1.content.data == content item2 = Item.create(u'New name') assert item2.name == u'New name' assert item2.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8' assert item2.content.data == content item3 = Item.create(u'Third') assert item3.name == u'Third' assert item3.meta[CONTENTTYPE] == 'text/x.moin.wiki;charset=utf-8' assert item3.content.data == content assert item1.rev.revid == item2.rev.revid == item3.rev.revid item4 = Item.create(u'Second') assert item4.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT
def testRevisionUpdate(self): """ creates two revisions of a container item """ item_name = u'ContainerItem2' item = Item.create(item_name, itemtype=ITEMTYPE_DEFAULT, contenttype=u'application/x-tar') filecontent = 'abcdefghij' content_length = len(filecontent) members = set(['example1.txt']) item.content.put_member('example1.txt', filecontent, content_length, expected_members=members) filecontent = 'AAAABBBB' content_length = len(filecontent) item.content.put_member('example1.txt', filecontent, content_length, expected_members=members) item = Item.create(item_name, contenttype=u'application/x-tar') assert item.content.get_member('example1.txt').read() == filecontent
def test_InlineInclude(self): update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'before {{page2}} after') # transclude single paragraph as inline update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Single line') rendered = Item.create(u'page1').content._render_data() assert '<p>before <span class="moin-transclusion" data-href="/page2">Single line</span> after</p>' in rendered # transclude multiple paragraphs as block update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Two\n\nParagraphs') rendered = Item.create(u'page1').content._render_data() assert '<p>before </p><div class="moin-transclusion" data-href="/page2"><p>Two</p><p>Paragraphs</p></div><p> after</p></div>' in rendered # transclude single paragraph with internal markup as inline update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"this text contains ''italic'' string") rendered = Item.create(u'page1').content._render_data() assert 'before <span class="moin-transclusion" data-href="/page2">this text contains <em>italic</em>' in rendered # transclude single paragraph as only content within a paragraph update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Content of page2 is\n\n{{page2}}') update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Single Line") rendered = Item.create(u'page1').content._render_data() assert '<p>Content of page2 is</p><p><span class="moin-transclusion" data-href="/page2">Single Line</span></p>' in rendered # transclude single row table within a paragraph, block element forces paragraph to be split into 2 parts update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'before {{page2}} after') update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"|| table || cell ||") rendered = Item.create(u'page1').content._render_data() assert '<p>before </p><div class="moin-transclusion" data-href="/page2"><table' in rendered assert '</table></div><p> after</p>' in rendered assert rendered.count('<table') == 1 # transclude two row table within a paragraph, block element forces paragraph to be split into 2 parts update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'before {{page2}} after') update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"|| this || has ||\n|| two || rows ||") rendered = Item.create(u'page1').content._render_data() # inclusion of block item within a paragraph results in a before and after p assert '<p>before </p><div class="moin-transclusion" data-href="/page2"><table' in rendered assert '</table></div><p> after</p>' in rendered assert rendered.count('<table') == 1 # transclude nonexistent item update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'before {{nonexistent}} after') rendered = Item.create(u'page1').content._render_data() assert '<p>before <span class="moin-transclusion" data-href="/nonexistent"><a href="/+modify/nonexistent">' in rendered assert '</a></span> after</p>' in rendered # transclude empty item update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text {{page2}} text') update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"") rendered = Item.create(u'page1').content._render_data() assert '<p>text <span class="moin-transclusion" data-href="/page2"></span> text</p>' in rendered
def do_show(self, revid): blog_item_name = self.name.rsplit('/', 1)[0] try: blog_item = Item.create(blog_item_name) except AccessDenied: abort(403) if not isinstance(blog_item, Blog): # The parent item of this blog entry item is not a Blog item. abort(403) return render_template('blog/entry.html', item_name=self.name, fqname=blog_item.fqname, blog_item=blog_item, blog_entry_item=self, item=self, )
def test_validjson(): """ Tests for changes to metadata when modifying an item. Does not apply to usersettings form. """ app.cfg.namespace_mapping = [('', 'default_backend'), ('ns1/', 'default_backend'), ('users/', 'other_backend')] item = Item.create('users/existingname') meta = {NAMESPACE: 'users', CONTENTTYPE: 'text/plain;charset=utf-8'} become_trusted() item._save(meta, data='This is a valid Item.') valid_itemid = 'a1924e3d0a34497eab18563299d32178' # ('names', 'namespace', 'field', 'value', 'result') tests = [ (['somename', '@revid'], '', '', 'somename', False), # item names cannot begin with @ # TODO for above? - create item @x, get error message, change name in meta to xx, get an item with names @40x and alias of xx (['bar', 'ns1'], '', '', 'bar', False ), # item names cannot match namespace names (['foo', 'foo', 'bar'], '', '', 'foo', False), # names in the name list must be unique. ( ['ns1ns2ns3', 'ns1/subitem'], '', '', 'valid', False ), # Item names must not match with existing namespaces; items cannot be in 2 namespaces ( ['foobar', 'validname'], '', ITEMID, valid_itemid + '8080', False ), # attempts to change itemid in meta result in "Item(s) named foobar, validname already exist." (['barfoo', 'validname'], '', ITEMID, valid_itemid.replace('a', 'y'), False), # similar to above ( [], '', 'itemid', valid_itemid, True ), # deleting all names from the metadata of an existing item will make it nameless, succeeds (['existingname'], 'users', '', 'existingname', False), # item already exists ] for name, namespace, field, value, result in tests: meta = {NAME: name, NAMESPACE: namespace} x = JSON(json.dumps(meta)) y = Names(name) state = dict(fqname=CompositeName(namespace, field, value), itemid=None, meta=meta) value = x.validate(state) and y.validate(state) assert value == result
def test_get_subscribers(self, item, item_name, namespace, tag_name): users = get_subscribers(**item.meta) assert users == set() name1 = u'baz' password = u'password' email1 = u'*****@*****.**' name2 = u"bar" email2 = u"*****@*****.**" name3 = u"barbaz" email3 = u"*****@*****.**" user.create_user(username=name1, password=password, email=email1, validate=False, locale=u'en') user1 = user.User(name=name1, password=password) user.create_user(username=name2, password=password, email=email2, validate=False) user2 = user.User(name=name2, password=password) user.create_user(username=name3, password=password, email=email3, verify_email=True, locale=u"en") user3 = user.User(name=name3, password=password, email=email3) subscribers = get_subscribers(**item.meta) assert subscribers == set() namere = r'.*' nameprefix = u"fo" subscription_lists = [ ["{0}:{1}".format(ITEMID, item.meta[ITEMID])], ["{0}:{1}:{2}".format(TAGS, namespace, tag_name)], ["{0}:{1}:{2}".format(NAME, namespace, item_name)], ["{0}:{1}:{2}".format(NAMERE, namespace, namere)], ["{0}:{1}:{2}".format(NAMEPREFIX, namespace, nameprefix)], ] users = [user1, user2, user3] expected_names = {user1.name0, user2.name0} for subscriptions in subscription_lists: for user_ in users: user_.profile._meta[SUBSCRIPTIONS] = subscriptions user_.save(force=True) subscribers = get_subscribers(**item.meta) subscribers_names = {subscriber.name for subscriber in subscribers} assert subscribers_names == expected_names meta = {CONTENTTYPE: u'text/plain;charset=utf-8', ACL: u"{0}: All:read,write".format(user1.name0)} item._save(meta, comment=u"") item = Item.create(item_name) subscribers = get_subscribers(**item.meta) assert {subscriber.name for subscriber in subscribers} == {user2.name0}
def modify_acl(item_name): fqname = split_fqname(item_name) item = Item.create(item_name) meta = dict(item.meta) new_acl = request.form.get(fqname.fullname) is_valid = acl_validate(new_acl) if is_valid: if new_acl in ('Empty', ''): meta[ACL] = '' elif new_acl == 'None' and ACL in meta: del(meta[ACL]) else: meta[ACL] = new_acl item._save(meta=meta) flash(L_("Success! ACL saved.<br>Item: %(item_name)s<br>ACL: %(acl_rule)s", item_name=fqname.fullname, acl_rule=new_acl), "info") else: flash(L_("Nothing changed, invalid ACL.<br>Item: %(item_name)s<br>ACL: %(acl_rule)s", item_name=fqname.fullname, acl_rule=new_acl), "error") return redirect(url_for('.item_acl_report'))
def get_item_names(self, name='', startswith='', kind='files', skiptag=''): """ For the specified item, return the fullname of matching descndents. Input: name: the name of the item to get. If '' is passed, then the top-level item is used. startwith: a substring the matching pages must begin with. If no value is specified, then all pages are returned. kind: the kind of page to return. Valid values include: files: decendents that do not contain decendents. (default) dirs: decendents that contain decendents. both: both 'files' and 'dirs', with duplicates removed. skiptag: skip items having this tag Output: A List of descendent items using their "fullname" value """ try: item = Item.create(name) except AccessDenied: abort(403) dirs, files = item.get_index(startswith) item_names = [] if not kind or kind == "files" or kind == "both": for item in files: if skiptag and TAGS in item.meta and skiptag in item.meta[TAGS]: continue item_names.append(item.fullname.value) if kind == "dirs" or kind == "both": for item in dirs: if skiptag and skiptag in item.meta[TAGS]: continue item_names.append(item.fullname.value) if kind == "both": item_names = list(set(item_names)) # remove duplicates return item_names
def build_mixed_index(basename, spec): """ Build a list of MixedIndexEntry by hand, useful as a test helper for index testing. The mixed index is a combo of dirs and files with empty meta (dirs) or reduced meta (files). :spec is a list of (relname, hassubitem) tuples. """ files = [(MixedIndexEntry( relname, CompositeName(NAMESPACE_DEFAULT, NAME_EXACT, '/'.join( (basename, relname))), Item.create('/'.join((basename, relname))).meta, hassubitem)) for relname, hassubitem in spec] return [ (MixedIndexEntry(f.relname, f.fullname, {} if f.hassubitems else {key: f.meta[key] for key in (CONTENTTYPE, ITEMTYPE)}, f.hassubitems)) for f in files ]
def _render_data_diff_html(self, oldrev, newrev, template, rev_links={}): """ Render HTML formatted meta and content diff of 2 revisions :param oldrev: old revision object :param newrev: new revision object :param template: name of the template to be rendered :return: HTML data with meta and content diff """ from moin.items import Item # XXX causes import error if placed near top diffs = self._get_data_diff_html(oldrev.data, newrev.data) item = Item.create(newrev.meta['name'][0], rev_id=newrev.meta['revid']) rendered = Markup(item.content._render_data()) return render_template(template, item_name=self.name, oldrev=oldrev, newrev=newrev, diffs=diffs, rendered=rendered, rev_links=rev_links, )
def test_IncludeAsLinkAlternate(self): # the 3rd parameter, u'', should be a binary string defining a png image, but it is not needed for this simple test update_item(u'logo.png', {CONTENTTYPE: u'image/png'}, u'') update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Single Line") # image as link alternate update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"text [[page2|{{logo.png}}]] text") rendered = Item.create(u'page1').content._render_data() assert '<p>text <a href="/page2"><span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src="' in rendered assert '/logo.png" /></span></a> text</p>' in rendered # link alternate with image embedded in markup update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"text [[page2|plain '''bold {{logo.png}} bold''' plain]] text") rendered = Item.create(u'page1').content._render_data() assert '<p>text <a href="/page2">plain <strong>bold <span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src="' in rendered assert '/logo.png" /></span> bold</strong> plain</a> text</p>' in rendered # nonexistent image used in link alternate # XXX html validation errora: A inside A - the image alternate turns into an A-tag to create the non-existant image. Error is easily seen. # IE9, Firefox, Chrome, Safari, and Opera display this OK; the only usable hyperlink is to create the missing image. update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"text [[page2|{{logoxxx.png}}]] text") rendered = Item.create(u'page1').content._render_data() assert '<p>text <a href="/page2"><span class="moin-transclusion" data-href="/logoxxx.png"><a href="/+modify/logoxxx.png">' in rendered assert '</a></span></a> text</p>' in rendered # image used as alternate to nonexistent page update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"text [[page2xxx|{{logo.png}}]] text") rendered = Item.create(u'page1').content._render_data() assert '<p>text <a class="moin-nonexistent" href="/page2xxx"><span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src="' in rendered assert '/logo.png" /></span></a> text</p>' in rendered # transclude block elem as link alternate to nonexistent page # XXX html validation errors, block element inside A. # IE9, Firefox, Chrome, Safari, and Opera display this OK; the hyperlink is the entire div enclosing the block elem update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text [[MyPage|{{page2}}]] text') update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"Double\n\nLine") rendered = Item.create(u'page1').content._render_data() assert '<p>text <a class="moin-nonexistent" href="/MyPage"><div class="moin-transclusion" data-href="/page2"><p>Double</p><p>Line</p></div></a> text</p>' in rendered # transclude empty item as link alternate to nonexistent page # hyperlink will be empty span and invisible update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text [[MyPage|{{page2}}]] text') update_item(u'page2', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u"") rendered = Item.create(u'page1').content._render_data() assert '<p>text <a class="moin-nonexistent" href="/MyPage"><span class="moin-transclusion" data-href="/page2"></span></a> text</p>' in rendered # transclude external page as link alternate to nonexistent page update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text [[MyPage|{{http://moinmo.in}}]] text') rendered = Item.create(u'page1').content._render_data() assert '<p>text <a class="moin-nonexistent" href="/MyPage"><object class="moin-transclusion" data="http://moinmo.in" data-href="http://moinmo.in">http://moinmo.in</object></a> text</p>' in rendered
def macro(self, content, arguments, page_url, alternative): if arguments: item_count = int(arguments[0]) else: item_count = 1 all_item_names = self.get_item_names() # Now select random item from the full list, and if it exists and # we can read it, save. random_item_names = [] found = 0 while found < item_count and all_item_names: # Take one random item from the list item_name = random.choice(all_item_names) all_item_names.remove(item_name) # Filter out items the user may not read. try: item = Item.create(item_name) random_item_names.append(item_name) found += 1 except AccessDenied: pass if not random_item_names: return random_item_names.sort() result = moin_page.span() for name in random_item_names: link = unicode(Iri(scheme=u'wiki', authority=u'', path=u'/' + name)) result.append( moin_page.a(attrib={xlink.href: link}, children=[name])) result.append(", ") del result[-1] # kill last comma return result
def test_rename_recursion(self): update_item(u'Page', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'Page 1') update_item(u'Page/Child', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'this is child') update_item(u'Page/Child/Another', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'another child') item = Item.create(u'Page') item.rename(u'Renamed_Page', comment=u'renamed') # items at original name and its contents after renaming item = Item.create(u'Page') assert item.name == u'Page' assert item.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT item = Item.create(u'Page/Child') assert item.name == u'Page/Child' assert item.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT item = Item.create(u'Page/Child/Another') assert item.name == u'Page/Child/Another' assert item.meta[CONTENTTYPE] == CONTENTTYPE_NONEXISTENT # item at new name and its contents after renaming item = Item.create(u'Renamed_Page') assert item.name == u'Renamed_Page' assert item.meta[NAME_OLD] == [u'Page'] assert item.meta[COMMENT] == u'renamed' assert item.content.data == u'Page 1' item = Item.create(u'Renamed_Page/Child') assert item.name == u'Renamed_Page/Child' assert item.meta[NAME_OLD] == [u'Page/Child'] assert item.meta[COMMENT] == u'renamed' assert item.content.data == u'this is child' item = Item.create(u'Renamed_Page/Child/Another') assert item.name == u'Renamed_Page/Child/Another' assert item.meta[NAME_OLD] == [u'Page/Child/Another'] assert item.meta[COMMENT] == u'renamed' assert item.content.data == u'another child'
def test_data_conversion(self): item_name = 'Text_Item' item = Item.create(item_name, ITEMTYPE_DEFAULT, 'text/plain;charset=utf-8') test_text = 'This \n is \n a \n Test' # test for data_internal_to_form result = Text.data_internal_to_form(item.content, test_text) expected = 'This \r\n is \r\n a \r\n Test' assert result == expected # test for data_form_to_internal test_form = 'This \r\n is \r\n a \r\n Test' result = Text.data_form_to_internal(item.content, test_text) expected = test_text assert result == expected # test for data_internal_to_storage result = Text.data_internal_to_storage(item.content, test_text) expected = b'This \r\n is \r\n a \r\n Test' assert result == expected # test for data_storage_to_internal data_storage = b'This \r\n is \r\n a \r\n Test' result = Text.data_storage_to_internal(item.content, data_storage) expected = test_text assert result == expected
def modify_acl(item_name): fqname = split_fqname(item_name) item = Item.create(item_name) meta = dict(item.meta) old_acl = meta.get(ACL, '') new_acl = request.form.get(fqname.fullname) is_valid = acl_validate(new_acl) if is_valid: if new_acl in ('Empty', ''): meta[ACL] = '' elif new_acl == 'None' and ACL in meta: del(meta[ACL]) else: meta[ACL] = new_acl try: item._save(meta=meta) except AccessDenied: # superuser viewed item acl report and tried to change acl but lacked admin permission flash(L_("Failed! Not authorized.<br>Item: %(item_name)s<br>ACL: %(acl_rule)s", item_name=fqname.fullname, acl_rule=old_acl), "error") return redirect(url_for('.item_acl_report')) flash(L_("Success! ACL saved.<br>Item: %(item_name)s<br>ACL: %(acl_rule)s", item_name=fqname.fullname, acl_rule=new_acl), "info") else: flash(L_("Nothing changed, invalid ACL.<br>Item: %(item_name)s<br>ACL: %(acl_rule)s", item_name=fqname.fullname, acl_rule=new_acl), "error") return redirect(url_for('.item_acl_report'))
def nuke_item(name): """ complete destroys an item """ item = Item.create(name) item.destroy()
def build_index(basename, relnames): """ Build a list of IndexEntry by hand, useful as a test helper. """ return [(IndexEntry(relname, CompositeName(NAMESPACE_DEFAULT, NAME_EXACT, '/'.join((basename, relname))), Item.create('/'.join((basename, relname))).meta)) for relname in relnames]
def build_mixed_index(basename, spec): """ Build a list of MixedIndexEntry by hand, useful as a test helper. :spec is a list of (relname, hassubitem) tuples. """ return [(MixedIndexEntry(relname, CompositeName(NAMESPACE_DEFAULT, NAME_EXACT, '/'.join((basename, relname))), Item.create('/'.join((basename, relname))).meta, hassubitem)) for relname, hassubitem in spec]
def item(self, item_name, meta): item = Item.create(item_name) item._save(meta) return Item.create(item_name)
def recurse(self, elem, page_href): # on first call, elem.tag.name=='page'. # Descendants (body, div, p, include, page, etc.) are processed by recursing through DOM # stack is used to detect transclusion loops page_href_new = elem.get(moin_page.page_href) if page_href_new: page_href_new = Iri(page_href_new) if page_href_new != page_href: page_href = page_href_new self.stack.append(page_href) else: self.stack.append(None) else: self.stack.append(None) try: if elem.tag == xinclude.include: # we have already recursed several levels and found a transclusion: "{{SomePage}}" or <<Include(...)>> # process the transclusion and add it to the DOM. Subsequent recursions will traverse through # the transclusion's elements. href = elem.get(xinclude.href) xpointer = elem.get(xinclude.xpointer) xp_include_pages = None xp_include_sort = None xp_include_items = None xp_include_skipitems = None xp_include_heading = None xp_include_level = None if xpointer: # we are working on an <<Include(abc)>> macro, not a {{transclusion}} xp = XPointer(xpointer) xp_include = None xp_namespaces = {} for entry in xp: uri = None name = entry.name.split(':', 1) if len(name) > 1: prefix, name = name uri = xp_namespaces.get(prefix, False) else: name = name[0] if uri is None and name == 'xmlns': d_prefix, d_uri = entry.data.split('=', 1) xp_namespaces[d_prefix] = d_uri elif uri == moin_page.namespace and name == 'include': xp_include = XPointer(entry.data) if xp_include: for entry in xp_include: name, data = entry.name, entry.data_unescape # TODO: These do not include all parameters in moin 1.9 Include macro docs: # <<Include(pagename, heading, level, from="regex", to="regex", sort=ascending|descending, items=n, skipitems=n, titlesonly, editlink)>> # these are currently unsupported in moin 2.0: from, to, titlesonly, editlink if name == 'pages': # pages == pagename in moin 1.9 xp_include_pages = data elif name == 'sort': xp_include_sort = data elif name == 'items': xp_include_items = int(data) elif name == 'skipitems': xp_include_skipitems = int(data) elif name == 'heading': xp_include_heading = data elif name == 'level': xp_include_level = data included_elements = [] if href: # We have a single page to transclude or include href = Iri(href) link = Iri(scheme='wiki', authority='') if href.scheme == 'wiki': if href.authority: raise ValueError( "can't handle xinclude for non-local authority" ) else: path = href.path[1:] elif href.scheme == 'wiki.local': page = page_href path = href.path if path[0] == '': # /subitem tmp = page.path[1:] tmp.extend(path[1:]) path = tmp elif path[0] == '..': # ../sisteritem path = page.path[1:] + path[1:] else: raise ValueError( "can't handle xinclude for schemes other than wiki or wiki.local" ) link.path = path if flaskg.user.may.read(unicode(path)): page = Item.create(unicode(path)) pages = ((page, link), ) else: # ACLs prevent user from viewing a transclusion - show message message = moin_page.p(children=(_( 'Access Denied, transcluded content suppressed.'))) attrib = {html.class_: 'warning'} div = ET.Element(moin_page.div, attrib, children=(message, )) container = ET.Element(moin_page.body, children=(div, )) return [ container, 0 ] # replace transclusion with container's child elif xp_include_pages: # we have regex of pages to include: <<Include(^qqq)>> query = And([ Term(WIKINAME, app.cfg.interwikiname), Regex(NAME_EXACT, xp_include_pages) ]) reverse = xp_include_sort == 'descending' results = flaskg.storage.search(query, sortedby=NAME_EXACT, reverse=reverse, limit=None) pagelist = [result.name for result in results] if xp_include_skipitems is not None: pagelist = pagelist[xp_include_skipitems:] if xp_include_items is not None: pagelist = pagelist[xp_include_items + 1:] pages = ((Item.create(p), Iri(scheme='wiki', authority='', path='/' + p)) for p in pagelist) if not pagelist: msg = _( 'Error: no items found matching "<<Include({0})>>"' ).format(xp_include_pages) attrib = {html.class_: 'moin-error'} strong = ET.Element(moin_page.strong, attrib, (msg, )) included_elements.append(strong) for page, p_href in pages: if p_href.path[0] != '/': p_href.path = IriPath('/' + '/'.join(p_href.path)) if p_href in self.stack: # we have a transclusion loop, create an error message showing list of pages forming loop loop = self.stack[self.stack.index(p_href):] loop = [ u'{0}'.format(ref.path[1:]) for ref in loop if ref is not None ] + [page.name] msg = u'Error: Transclusion loop via: ' + u', '.join( loop) attrib = {html.class_: 'moin-error'} strong = ET.Element(moin_page.strong, attrib, (msg, )) included_elements.append(strong) continue if xp_include_heading is not None: attrib = {xlink.href: p_href} children = (xp_include_heading or page.name, ) elem_a = ET.Element(moin_page.a, attrib, children=children) attrib = { moin_page.outline_level: xp_include_level or '1' } elem_h = ET.Element(moin_page.h, attrib, children=(elem_a, )) included_elements.append(elem_h) page_doc = page.content.internal_representation( attributes=Arguments(keyword=elem.attrib)) if isinstance(page.rev.data, file): page.rev.data.close() self.recurse(page_doc, page_href) # The href needs to be an absolute URI, without the prefix "wiki://" page_doc = mark_item_as_transclusion(page_doc, p_href.path) included_elements.append(page_doc) if len(included_elements) > 1: # use a div as container result = ET.Element(moin_page.div) result.extend(included_elements) elif included_elements: result = included_elements[0] else: result = None # end of processing for transclusion; the "result" will get inserted into the DOM below return result # Traverse the DOM by calling self.recurse with each child of the current elem. # Starting elem.tag.name=='page'. container = [] i = 0 while i < len(elem): child = elem[i] if isinstance(child, ET.Node): ret = self.recurse(child, page_href) if ret: # Either child or a descendant of child is a transclusion. # See top of this script for notes on why these DOM adjustments are required. if isinstance(ret, ET.Node ) and elem.tag.name in NO_BLOCK_CHILDREN: body = ret[0] if len(body) == 0: # the transcluded item is empty, insert an empty span into DOM attrib = Attributes(ret).convert() elem[i] = ET.Element(moin_page.span, attrib=attrib) elif (isinstance(body[0], ET.Node) and (len(body) > 1 or body[0].tag.name not in ('p', 'object', 'a'))): # Complex case: "some text {{BlockItem}} more text" or "\n{{BlockItem}}\n" where # the BlockItem body contains multiple p's, a table, preformatted text, etc. # These block elements cannot be made a child of the current elem, so we create # a container to replace elem. # Create nodes to hold any siblings before and after current child (elem[i]) before = copy.deepcopy(elem) after = copy.deepcopy(elem) before[:] = elem[0:i] after[:] = elem[i + 1:] if len(before): # there are siblings before transclude, save them in container container.append(before) new_trans_ptr = len(container) # get attributes from page node; # we expect {class: "moin-transclusion"; data-href: "http://some.org/somepage"} attrib = Attributes(ret).convert() # current elem will likely be replaced by container so we need to copy data-lineno attr if html.data_lineno in elem.attrib: attrib[html.data_lineno] = elem.attrib[ html.data_lineno] # make new div node to hold transclusion, copy children, and save in container div = ET.Element(moin_page.div, attrib=attrib, children=body[:]) container.append( div) # new_trans_ptr is index to this if len(after): container.append(after) if elem.tag.name == 'a': # invalid input [[MyPage|{{BlockItem}}]], # best option is to retain A-tag and fail html validation # TODO: error may not be obvious to user - add error message elem[i] = div else: # move up 1 level in recursion where elem becomes the child and # is usually replaced by container return [container, new_trans_ptr] else: # default action for inline transclusions or odd things like circular transclusion error messages classes = child.attrib.get(html.class_, '').split() classes += ret.attrib.get(html.class_, '').split() ret.attrib[html.class_] = ' '.join(classes) elem[i] = ret elif isinstance(ret, types.ListType): # a container has been returned. # Note: there are multiple places where a container may be constructed ret_container, trans_ptr = ret # trans_ptr points to the transclusion within ret_container. # Here the transclusion will always contain a block level element if elem.tag.name in NO_BLOCK_CHILDREN: # Complex case, transclusion effects grand-parent, great-grand-parent, e.g.: # "/* comment {{BlockItem}} */" or "text ''italic {{BlockItem}} italic'' text" # elem is an inline element, build a bigger container to replace elem's parent, before = copy.deepcopy(elem) after = copy.deepcopy(elem) before[:] = elem[0:i] + ret_container[ 0:trans_ptr] after[:] = ret_container[trans_ptr + 1:] + elem[i + 1:] if len(before): container.append(before) new_trans_ptr = len(container) # child may have classes like "comment" that must be added to transcluded element classes = child.attrib.get( moin_page.class_, '').split() # must use moin_page.class_ above, but use html.class below per html_out.py code classes += ret_container[trans_ptr].attrib.get( html.class_, '').split() ret_container[trans_ptr].attrib[ html.class_] = ' '.join(classes) container.append(ret_container[trans_ptr] ) # the transclusion if len(after): container.append(after) return [container, new_trans_ptr] else: # elem is a block element for grandchild in child: if isinstance( grandchild, ET.Node ) and grandchild.tag.name == u'include': # the include may have classes that must be added to transcluded element classes = grandchild.attrib.get( html.class_, '').split() classes += ret_container[ trans_ptr].attrib.get( html.class_, '').split() ret_container[trans_ptr].attrib[ html.class_] = ' '.join(classes) # replace child element with the container generated in lower recursion elem[i:i + 1] = ret_container # elem[i] is the child else: # default action for any ret not fitting special cases above, # e.g. tranclusion is within a table cell elem[i] = ret # we are finished with this child, advance to next sibling i += 1 finally: self.stack.pop()
def atom(item_name): # Currently atom feeds behave in the fol. way # - Text diffs are shown in a side-by-side fashion # - The current binary item is fully rendered in the feed # - Image(binary)'s diff is shown using PIL # - First item is always rendered fully # - Revision meta(id, size and comment) is shown for parent and current revision query = Term(WIKINAME, app.cfg.interwikiname) if item_name: query = And([ query, Term(NAME_EXACT, item_name), ]) revs = list( flaskg.storage.search(query, idx_name=LATEST_REVS, sortedby=[MTIME], reverse=True, limit=1)) if revs: rev = revs[0] cid = cache_key(usage="atom", revid=rev.revid, item_name=item_name) content = app.cache.get(cid) else: content = None cid = None if content is None: if not item_name: title = u"{0}".format(app.cfg.sitename) else: title = u"{0} - {1}".format(app.cfg.sitename, item_name) feed = AtomFeed(title=title, feed_url=request.url, url=request.host_url) query = Term(WIKINAME, app.cfg.interwikiname) if item_name: query = And([ query, Term(NAME_EXACT, item_name), ]) history = flaskg.storage.search(query, idx_name=ALL_REVS, sortedby=[MTIME], reverse=True, limit=100) for rev in history: name = rev.name item = rev.item this_revid = rev.meta[REVID] previous_revid = rev.meta.get(PARENTID) this_rev = rev try: hl_item = Item.create(name, rev_id=this_revid) if previous_revid is not None: # HTML diff for subsequent revisions previous_rev = item[previous_revid] content = hl_item.content._render_data_diff_atom( previous_rev, this_rev) else: # full html rendering for new items content = render_template( 'atom.html', get='first_revision', rev=this_rev, content=Markup(hl_item.content._render_data()), revision=this_revid) content_type = 'html' except Exception as e: logging.exception("content rendering crashed") content = _(u'MoinMoin feels unhappy.') content_type = 'text' author = get_editor_info(rev.meta, external=True) rev_comment = rev.meta.get(COMMENT, '') if rev_comment: # Trim down extremely long revision comment if len(rev_comment) > 80: content = render_template('atom.html', get='comment_cont_merge', comment=rev_comment[79:], content=Markup(content)) rev_comment = u"{0}...".format(rev_comment[:79]) feed_title = u"{0} - {1}".format(author.get(NAME, ''), rev_comment) else: feed_title = u"{0}".format(author.get(NAME, '')) if not item_name: feed_title = u"{0} - {1}".format(name, feed_title) feed.add( title=feed_title, title_type='text', summary=content, summary_type=content_type, author=author, url=url_for_item(name, rev=this_revid, _external=True), updated=datetime.fromtimestamp(rev.meta[MTIME]), ) content = feed.to_string() # Hack to add XSLT stylesheet declaration since AtomFeed doesn't allow this content = content.split("\n") content.insert(1, render_template('atom.html', get='xml')) content = "\n".join(content) if cid is not None: app.cache.set(cid, content) return Response(content, content_type='application/atom+xml')