Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 # Copyright: 2012-2013 MoinMoin:PavelSviderski | |
1 # Copyright: 2012 MoinMoin:CheerXiao | 2 # Copyright: 2012 MoinMoin:CheerXiao |
2 # Copyright: 2009 MoinMoin:ThomasWaldmann | 3 # Copyright: 2009 MoinMoin:ThomasWaldmann |
3 # Copyright: 2009-2011 MoinMoin:ReimarBauer | 4 # Copyright: 2009-2011 MoinMoin:ReimarBauer |
4 # Copyright: 2009 MoinMoin:ChristopherDenter | 5 # Copyright: 2009 MoinMoin:ChristopherDenter |
5 # Copyright: 2008,2009 MoinMoin:BastianBlank | 6 # Copyright: 2008,2009 MoinMoin:BastianBlank |
6 # Copyright: 2010 MoinMoin:ValentinJaniaut | 7 # Copyright: 2010 MoinMoin:ValentinJaniaut |
7 # Copyright: 2010 MoinMoin:DiogenesAugusto | 8 # Copyright: 2010 MoinMoin:DiogenesAugusto |
8 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details. | 9 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details. |
9 | 10 |
10 """ | 11 """ |
11 MoinMoin - high-level (frontend) items | 12 MoinMoin - high-level (frontend) items |
12 | 13 |
13 While MoinMoin.storage cares for backend storage of items, | 14 While MoinMoin.storage cares for backend storage of items, |
14 this module cares for more high-level, frontend items, | 15 this module cares for more high-level, frontend items, |
15 e.g. showing, editing, etc. of wiki items. | 16 e.g. showing, editing, etc. of wiki items. |
16 | 17 |
17 Each class in this module corresponds to an itemtype. | 18 Each class in this module corresponds to an itemtype. |
18 """ | 19 """ |
19 | 20 |
20 import json | 21 import json |
21 from StringIO import StringIO | 22 from StringIO import StringIO |
22 from collections import namedtuple | 23 from collections import namedtuple |
23 from operator import attrgetter | 24 from operator import attrgetter |
24 | 25 |
25 from flask import current_app as app | 26 from flask import current_app as app |
26 from flask import g as flaskg | 27 from flask import g as flaskg |
27 from flask import request, Response, redirect, abort, escape | 28 from flask import request, Response, redirect, abort, escape |
28 | 29 |
29 from flatland import Form | 30 from flatland import Form, Unset |
30 | 31 |
31 from jinja2 import Markup | 32 from jinja2 import Markup |
32 | 33 |
33 from whoosh.query import Term, Prefix, And, Or, Not | 34 from whoosh.query import Term, Prefix, And, Or, Not |
34 | 35 |
35 from MoinMoin import log | 36 from MoinMoin import log |
36 logging = log.getLogger(__name__) | 37 logging = log.getLogger(__name__) |
37 | 38 |
38 from MoinMoin.security.textcha import TextCha, TextChaizedForm | 39 from MoinMoin.security.textcha import TextCha, TextChaizedForm |
39 from MoinMoin.signalling import item_modified | 40 from MoinMoin.signalling import item_modified |
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
388 | 389 |
389 def destroy(self, comment=u'', destroy_item=False): | 390 def destroy(self, comment=u'', destroy_item=False): |
390 # called from destroy UI/POST | 391 # called from destroy UI/POST |
391 if destroy_item: | 392 if destroy_item: |
392 # destroy complete item with all revisions, metadata, etc. | 393 # destroy complete item with all revisions, metadata, etc. |
393 self.rev.item.destroy_all_revisions() | 394 self.rev.item.destroy_all_revisions() |
394 else: | 395 else: |
395 # just destroy this revision | 396 # just destroy this revision |
396 self.rev.item.destroy_revision(self.rev.revid) | 397 self.rev.item.destroy_revision(self.rev.revid) |
397 | 398 |
398 def modify(self, meta, data, comment=u'', contenttype_guessed=None, contentt ype_qs=None): | 399 def modify(self, meta, data, comment=u'', contenttype_guessed=None, **update _meta): |
399 if contenttype_qs: | 400 meta = dict(meta) # we may get a read-only dict-like, copy it |
400 # we use querystring param to FORCE content type | 401 # get rid of None values |
401 meta[CONTENTTYPE] = contenttype_qs | 402 update_meta = {key:value for key, value in update_meta.items() if value is not None} |
Thomas.J.Waldmann
2013/07/29 22:48:59
key: value
pep8
| |
402 | 403 meta.update(update_meta) |
403 return self._save(meta, data, contenttype_guessed=contenttype_guessed, c omment=comment) | 404 return self._save(meta, data, contenttype_guessed=contenttype_guessed, c omment=comment) |
404 | 405 |
405 class _ModifyForm(BaseModifyForm): | 406 class _ModifyForm(BaseModifyForm): |
406 """ | 407 """ |
407 ModifyForm (the form used on +modify view), sans the content part. | 408 ModifyForm (the form used on +modify view), sans the content part. |
408 Combined dynamically with the ModifyForm of the Content subclass in | 409 Combined dynamically with the ModifyForm of the Content subclass in |
409 Contentful.ModifyForm. | 410 Contentful.ModifyForm. |
410 | 411 |
411 Subclasses of Contentful should generally override this instead of | 412 Subclasses of Contentful should generally override this instead of |
412 ModifyForm. | 413 ModifyForm. |
(...skipping 22 matching lines...) Expand all Loading... | |
435 """ | 436 """ |
436 Dump useful data out of :self. :item contains the old item and | 437 Dump useful data out of :self. :item contains the old item and |
437 should not be the primary data source; but it can be useful in case | 438 should not be the primary data source; but it can be useful in case |
438 the data in :self is not sufficient. | 439 the data in :self is not sufficient. |
439 | 440 |
440 :returns: a tuple (meta, data, contenttype_guessed, comment), | 441 :returns: a tuple (meta, data, contenttype_guessed, comment), |
441 suitable as arguments of the same names to pass to | 442 suitable as arguments of the same names to pass to |
442 item.modify | 443 item.modify |
443 """ | 444 """ |
444 meta = self['meta_form'].value.copy() | 445 meta = self['meta_form'].value.copy() |
445 meta.update(item.meta_text_to_dict(self['extra_meta_text'].value)) | 446 if self['extra_meta_text'].raw is not Unset: # it's an optional fie ld |
447 meta.update(item.meta_text_to_dict(self['extra_meta_text'].value )) | |
446 data, contenttype_guessed = self['content_form']._dump(item.content) | 448 data, contenttype_guessed = self['content_form']._dump(item.content) |
447 comment = self['comment'].value | 449 comment = self['comment'].value |
448 return meta, data, contenttype_guessed, comment | 450 return meta, data, contenttype_guessed, comment |
449 | 451 |
450 def do_modify(self): | 452 def do_modify(self): |
451 """ | 453 """ |
452 Handle +modify requests, both GET and POST. | 454 Handle +modify requests, both GET and POST. |
453 | 455 |
454 This method should be overridden in subclasses, providing polymorphic | 456 This method should be overridden in subclasses, providing polymorphic |
455 behavior for the +modify view. | 457 behavior for the +modify view. |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
674 itemtype=self.itemtype, | 676 itemtype=self.itemtype, |
675 rev=self.rev, | 677 rev=self.rev, |
676 contenttype=self.contenttype, | 678 contenttype=self.contenttype, |
677 templates=item_templates, | 679 templates=item_templates, |
678 first_rev_id=rev_ids and rev_ids[0], | 680 first_rev_id=rev_ids and rev_ids[0], |
679 last_rev_id=rev_ids and rev_ids[-1], | 681 last_rev_id=rev_ids and rev_ids[-1], |
680 meta_rendered='', | 682 meta_rendered='', |
681 data_rendered='', | 683 data_rendered='', |
682 ) | 684 ) |
683 | 685 |
684 def get_comment_items(self): | 686 def get_comment_item_ids(self): |
685 terms = [Term(WIKINAME, app.cfg.interwikiname), | 687 terms = [Term(WIKINAME, app.cfg.interwikiname), |
686 # Only comment items | 688 # Only comment items |
687 Term(ITEMTYPE, ITEMTYPE_COMMENT), | 689 Term(ITEMTYPE, ITEMTYPE_COMMENT), |
688 # Only comments for this item | 690 # Only comments for this item |
689 Term(COMMENT_FOR, self.meta[ITEMID]), | 691 Term(COMMENT_FOR, self.meta[ITEMID]), |
690 ] | 692 ] |
691 query = And(terms) | 693 query = And(terms) |
692 revs = flaskg.storage.search(query, sortedby=PTIME, limit=None) | 694 revs = flaskg.storage.search(query, sortedby=PTIME, limit=None) |
693 comment_items = [Item.create(rev.name) for rev in revs] | 695 comment_item_ids = [rev.item.itemid for rev in revs] |
Thomas.J.Waldmann
2013/06/28 22:45:30
as a general hint: be careful with creating potent
| |
694 return comment_items | 696 return comment_item_ids |
695 | 697 |
696 def do_show(self, revid): | 698 def do_show(self, revid): |
697 show_revision = revid != CURRENT | 699 show_revision = revid != CURRENT |
698 show_navigation = False # TODO | 700 show_navigation = False # TODO |
699 first_rev = last_rev = None # TODO | 701 first_rev = last_rev = None # TODO |
700 comments=self.get_comment_items() | 702 # TODO: comments should be a list of ITEMIDs, but currently we use a lis t |
703 # of ITEMNAMEs because we are not able to create an Item object by ITEMI D yet. | |
704 # TODO: filter out protected comments | |
705 comments = [flaskg.storage.document(itemid=itemid).name | |
706 for itemid in self.get_comment_item_ids()] | |
701 return render_template(self.show_template, | 707 return render_template(self.show_template, |
702 item=self, item_name=self.name, | 708 item=self, item_name=self.name, |
703 rev=self.rev, | 709 rev=self.rev, |
704 contenttype=self.contenttype, | 710 contenttype=self.contenttype, |
705 first_rev_id=first_rev, | 711 first_rev_id=first_rev, |
706 last_rev_id=last_rev, | 712 last_rev_id=last_rev, |
707 data_rendered=Markup(self.content._render_data()) , | 713 data_rendered=Markup(self.content._render_data()) , |
708 show_revision=show_revision, | 714 show_revision=show_revision, |
709 show_navigation=show_navigation, | 715 show_navigation=show_navigation, |
710 comments=comments, | 716 comments=comments, |
Thomas.J.Waldmann
2013/06/28 22:45:30
maybe this could be more dynamic later, so that co
| |
711 ) | 717 ) |
712 | 718 |
713 def do_modify(self): | 719 def do_modify(self): |
714 method = request.method | 720 method = request.method |
715 if method in ['GET', 'HEAD']: | 721 if method in ['GET', 'HEAD']: |
716 if isinstance(self.content, NonExistentContent): | 722 if isinstance(self.content, NonExistentContent): |
717 return render_template('modify_select_contenttype.html', | 723 return render_template('modify_select_contenttype.html', |
718 item_name=self.name, | 724 item_name=self.name, |
719 itemtype=self.itemtype, | 725 itemtype=self.itemtype, |
720 group_names=content_registry.group_names, | 726 group_names=content_registry.group_names, |
(...skipping 17 matching lines...) Expand all Loading... | |
738 else: | 744 else: |
739 # *Draw Applets POSTs more than once, redirecting would | 745 # *Draw Applets POSTs more than once, redirecting would |
740 # break them | 746 # break them |
741 return "OK" | 747 return "OK" |
742 form = self.ModifyForm.from_request(request) | 748 form = self.ModifyForm.from_request(request) |
743 state = dict(name=self.name, itemid=self.meta.get(ITEMID)) | 749 state = dict(name=self.name, itemid=self.meta.get(ITEMID)) |
744 if form.validate(state): | 750 if form.validate(state): |
745 meta, data, contenttype_guessed, comment = form._dump(self) | 751 meta, data, contenttype_guessed, comment = form._dump(self) |
746 contenttype_qs = request.values.get('contenttype') | 752 contenttype_qs = request.values.get('contenttype') |
747 try: | 753 try: |
748 self.modify(meta, data, comment, contenttype_guessed, conten ttype_qs) | 754 self.modify(meta, data, comment, contenttype_guessed, **{CON TENTTYPE: contenttype_qs}) |
749 except AccessDenied: | 755 except AccessDenied: |
750 abort(403) | 756 abort(403) |
751 else: | 757 else: |
752 return redirect(url_for_item(self.name)) | 758 return redirect(url_for_item(self.name)) |
753 return render_template(self.modify_template, | 759 return render_template(self.modify_template, |
754 item_name=self.name, | 760 item_name=self.name, |
755 rows_meta=str(ROWS_META), cols=str(COLS), | 761 rows_meta=str(ROWS_META), cols=str(COLS), |
756 form=form, | 762 form=form, |
757 search_form=None, | 763 search_form=None, |
758 ) | 764 ) |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
818 # pointless for non-existing items | 824 # pointless for non-existing items |
819 pass | 825 pass |
820 | 826 |
821 def destroy(self, comment=u'', destroy_item=False): | 827 def destroy(self, comment=u'', destroy_item=False): |
822 # pointless for non-existing items | 828 # pointless for non-existing items |
823 pass | 829 pass |
824 | 830 |
825 | 831 |
826 from ..util.pysupport import load_package_modules | 832 from ..util.pysupport import load_package_modules |
827 load_package_modules(__name__, __path__) | 833 load_package_modules(__name__, __path__) |
LEFT | RIGHT |