Left: | ||
Right: |
OLD | NEW |
---|---|
1 # Copyright: 2012 MoinMoin:CheerXiao | 1 # Copyright: 2012 MoinMoin:CheerXiao |
2 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details. | 2 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details. |
3 | 3 |
4 """ | 4 """ |
5 MoinMoin - Ticket itemtype | 5 MoinMoin - Ticket itemtype |
6 """ | 6 """ |
7 | 7 |
8 | 8 |
9 from __future__ import absolute_import, division | 9 from __future__ import absolute_import, division |
10 | 10 |
11 import time | 11 import time |
12 | 12 |
13 from flask import request, abort, redirect, url_for | 13 from flask import request, abort, redirect, url_for |
14 from flask import g as flaskg | 14 from flask import g as flaskg |
15 from flask import current_app as app | |
15 | 16 |
16 from jinja2 import Markup | 17 from jinja2 import Markup |
17 | 18 |
18 from whoosh.query import Term | 19 from whoosh.query import Term, Term, Prefix, Or, And |
19 | 20 |
20 from MoinMoin.i18n import L_ | 21 from MoinMoin.i18n import L_ |
21 from MoinMoin.themes import render_template | 22 from MoinMoin.themes import render_template |
22 from MoinMoin.forms import (Form, OptionalText, OptionalMultilineText, SmallNatu ral, Tags, | 23 from MoinMoin.forms import (File, Form, OptionalText, OptionalMultilineText, Sma llNatural, Tags, |
23 Reference, BackReference, SelectSubmit, Text) | 24 Reference, BackReference, SelectSubmit, Text) |
24 from MoinMoin.storage.middleware.protecting import AccessDenied | 25 from MoinMoin.storage.middleware.protecting import AccessDenied |
25 from MoinMoin.constants.keys import (ITEMTYPE, CONTENTTYPE, ITEMID, CURRENT, | 26 from MoinMoin.constants.keys import (ITEMTYPE, CONTENTTYPE, ITEMID, CURRENT, |
26 SUPERSEDED_BY, SUBSCRIPTIONS, DEPENDS_ON, N AME, SUMMARY, NAMESPACE) | 27 SUPERSEDED_BY, SUBSCRIPTIONS, DEPENDS_ON, |
28 NAME, SUMMARY, NAMESPACE, WIKINAME, NAME_EX ACT) | |
27 from MoinMoin.constants.contenttypes import CONTENTTYPE_USER | 29 from MoinMoin.constants.contenttypes import CONTENTTYPE_USER |
28 from MoinMoin.items import Item, Contentful, register, BaseModifyForm, get_itemt ype_specific_tags | 30 from MoinMoin.items import Item, Contentful, register, BaseModifyForm, get_itemt ype_specific_tags, IndexEntry |
29 from MoinMoin.items.content import NonExistentContent | 31 from MoinMoin.items.content import NonExistentContent |
30 from MoinMoin.util.interwiki import CompositeName | 32 from MoinMoin.util.interwiki import CompositeName |
31 | 33 |
32 | 34 |
33 ITEMTYPE_TICKET = u'ticket' | 35 ITEMTYPE_TICKET = u'ticket' |
34 | 36 |
35 USER_QUERY = Term(CONTENTTYPE, CONTENTTYPE_USER) | 37 USER_QUERY = Term(CONTENTTYPE, CONTENTTYPE_USER) |
36 TICKET_QUERY = Term(ITEMTYPE, ITEMTYPE_TICKET) | 38 TICKET_QUERY = Term(ITEMTYPE, ITEMTYPE_TICKET) |
37 | 39 |
38 Rating = SmallNatural.using(optional=True).with_properties(lower=1, upper=5) | 40 Rating = SmallNatural.using(optional=True).with_properties(lower=1, upper=5) |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
84 id_ = item.meta[ITEMID] | 86 id_ = item.meta[ITEMID] |
85 self['supersedes'].set(Term(SUPERSEDED_BY, id_)) | 87 self['supersedes'].set(Term(SUPERSEDED_BY, id_)) |
86 self['required_by'].set(Term(DEPENDS_ON, id_)) | 88 self['required_by'].set(Term(DEPENDS_ON, id_)) |
87 self['subscribers'].set(Term(SUBSCRIPTIONS, id_)) | 89 self['subscribers'].set(Term(SUBSCRIPTIONS, id_)) |
88 | 90 |
89 | 91 |
90 class TicketForm(BaseModifyForm): | 92 class TicketForm(BaseModifyForm): |
91 meta = TicketMetaForm | 93 meta = TicketMetaForm |
92 backrefs = TicketBackRefForm | 94 backrefs = TicketBackRefForm |
93 message = OptionalMultilineText.using(label=L_("Message")).with_properties(r ows=8, cols=80) | 95 message = OptionalMultilineText.using(label=L_("Message")).with_properties(r ows=8, cols=80) |
96 data_file = File.using(optional=True, label=L_('Upload file:')) | |
94 | 97 |
95 def _load(self, item): | 98 def _load(self, item): |
96 meta = item.prepare_meta_for_modify(item.meta) | 99 meta = item.prepare_meta_for_modify(item.meta) |
97 self['meta'].set(meta, 'duck') | 100 self['meta'].set(meta, 'duck') |
98 # XXX need a more explicit way to test for item creation/modification | 101 # XXX need a more explicit way to test for item creation/modification |
99 if ITEMID in item.meta: | 102 if ITEMID in item.meta: |
100 self['backrefs']._load(item) | 103 self['backrefs']._load(item) |
101 | 104 |
102 | 105 |
103 class TicketSubmitForm(TicketForm): | 106 class TicketSubmitForm(TicketForm): |
104 submit_label = L_("Submit ticket") | 107 submit_label = L_("Submit ticket") |
105 | 108 |
106 def _dump(self, item): | 109 def _dump(self, item): |
107 # initial metadata for Ticket-itemtyped item | 110 # initial metadata for Ticket-itemtyped item |
108 meta = { | 111 meta = { |
109 ITEMTYPE: item.itemtype, | 112 ITEMTYPE: item.itemtype, |
110 # XXX support other markups | 113 # XXX support other markups |
111 CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8', | 114 CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8', |
112 'closed': False, | 115 'closed': False, |
113 } | 116 } |
114 meta.update(self['meta'].value) | 117 meta.update(self['meta'].value) |
115 return meta, message_markup(self['message'].value) | 118 return meta, message_markup(self['message'].value), self['data_file'].va lue |
116 | 119 |
117 | 120 |
118 class TicketUpdateForm(TicketForm): | 121 class TicketUpdateForm(TicketForm): |
119 submit = SelectSubmit.valued('update', 'update_negate_status') | 122 submit = SelectSubmit.valued('update', 'update_negate_status') |
120 | 123 |
121 def _load(self, item): | 124 def _load(self, item): |
122 super(TicketUpdateForm, self)._load(item) | 125 super(TicketUpdateForm, self)._load(item) |
123 self['submit'].properties['labels'] = { | 126 self['submit'].properties['labels'] = { |
124 'update': L_('Update ticket'), | 127 'update': L_('Update ticket'), |
125 'update_negate_status': (L_('Update & reopen ticket') if item.meta.g et('closed') | 128 'update_negate_status': (L_('Update & reopen ticket') if item.meta.g et('closed') |
126 else L_('Update & close ticket')) | 129 else L_('Update & close ticket')) |
127 } | 130 } |
128 | 131 |
129 def _dump(self, item): | 132 def _dump(self, item): |
130 # Since the metadata form for tickets is an incomplete one, we load the | 133 # Since the metadata form for tickets is an incomplete one, we load the |
131 # original meta and update it with those from the metadata editor | 134 # original meta and update it with those from the metadata editor |
132 meta = item.meta_filter(item.prepare_meta_for_modify(item.meta)) | 135 meta = item.meta_filter(item.prepare_meta_for_modify(item.meta)) |
133 meta.update(self['meta'].value) | 136 meta.update(self['meta'].value) |
134 if self['submit'].value == 'update_negate_status': | 137 if self['submit'].value == 'update_negate_status': |
135 meta['closed'] = not meta.get('closed') | 138 meta['closed'] = not meta.get('closed') |
136 | 139 |
137 data = item.content.data_storage_to_internal(item.content.data) | 140 data = item.content.data_storage_to_internal(item.content.data) |
138 message = self['message'].value | 141 message = self['message'].value |
139 if message: | 142 if message: |
140 data += message_markup(message) | 143 data += message_markup(message) |
141 | 144 |
142 return meta, data | 145 return meta, data, self['data_file'].value |
143 | 146 |
144 | 147 |
145 # XXX Ideally we should generate DOM instead of moin wiki source. But | 148 # XXX Ideally we should generate DOM instead of moin wiki source. But |
146 # currently this is not very useful, since | 149 # currently this is not very useful, since |
147 # * DOM cannot be stored directly, it has to be converted to some markup first | 150 # * DOM cannot be stored directly, it has to be converted to some markup first |
148 # * DOM -> markup conversion is only available for moinwiki | 151 # * DOM -> markup conversion is only available for moinwiki |
149 | 152 |
150 # XXX How to do i18n on this? | 153 # XXX How to do i18n on this? |
151 | 154 |
152 def message_markup(message): | 155 def message_markup(message): |
153 return u'''{{{{{{#!wiki tip | 156 return u'''{{{{{{#!wiki tip |
154 %(author)s wrote on <<DateTime(%(timestamp)d)>>: | 157 %(author)s wrote on <<DateTime(%(timestamp)d)>>: |
155 | 158 |
156 %(message)s | 159 %(message)s |
157 }}}}}} | 160 }}}}}} |
158 ''' % dict(author=flaskg.user.name[0], timestamp=time.time(), message=message) | 161 ''' % dict(author=flaskg.user.name[0], timestamp=time.time(), message=message) |
159 | 162 |
160 | 163 |
164 def file_upload(self, data_file): | |
vipul
2015/06/11 14:15:13
creating item of file uploaded and adding 'refer_t
| |
165 item_name = data_file.filename | |
166 contenttype = data_file.content_type | |
Thomas.J.Waldmann
2015/06/11 15:44:44
is that always present or could it be empty / None
| |
167 data = data_file.stream | |
168 try: | |
vipul
2015/06/11 14:15:13
when I create and modify item again, the meta upda
| |
169 item = Item.create(item_name) | |
Thomas.J.Waldmann
2015/06/11 15:44:44
are you creating a named, toplevel item here? that
| |
170 update_meta = self.fqname.value········ | |
Thomas.J.Waldmann
2015/06/11 15:44:44
not sure why you assign to update_meta.
| |
171 item.modify({}, data, contenttype_guessed=contenttype, refer_to=update_m eta) | |
172 item = Item.create(item_name) | |
Thomas.J.Waldmann
2015/06/11 15:44:44
you already did that some lines above
| |
173 item.modify(item.meta, data, refer_to=update_meta) | |
Thomas.J.Waldmann
2015/06/11 15:44:44
same here
| |
174 except AccessDenied: | |
175 abort(403) | |
176 | |
177 | |
178 def get_files(self): | |
179 files = [] | |
180 query = Term(WIKINAME, app.cfg.interwikiname) | |
181 revs = flaskg.storage.search(query, limit=None) | |
182 for rev in revs: | |
Thomas.J.Waldmann
2015/06/11 15:44:44
this is very expensive.
and it's not really what
| |
183 names = rev.meta[NAME] | |
184 for name in names: | |
185 if rev.meta.get('refer_to') == self.fqname.value: | |
Thomas.J.Waldmann
2015/06/11 15:44:44
i told you about 5 times now not to use the name i
| |
186 fullname_fqname = CompositeName(rev.meta[NAMESPACE], NAME_EXACT, name) | |
187 files.append(IndexEntry(name, fullname_fqname, rev.meta)) | |
188 return files | |
189 | |
190 | |
161 @register | 191 @register |
162 class Ticket(Contentful): | 192 class Ticket(Contentful): |
163 itemtype = ITEMTYPE_TICKET | 193 itemtype = ITEMTYPE_TICKET |
164 display_name = L_('Ticket') | 194 display_name = L_('Ticket') |
165 description = L_('Ticket item') | 195 description = L_('Ticket item') |
166 submit_template = 'ticket/submit.html' | 196 submit_template = 'ticket/submit.html' |
167 modify_template = 'ticket/modify.html' | 197 modify_template = 'ticket/modify.html' |
168 | 198 |
169 def do_show(self, revid): | 199 def do_show(self, revid): |
170 if revid != CURRENT: | 200 if revid != CURRENT: |
171 # TODO When requesting a historical version, show a readonly view | 201 # TODO When requesting a historical version, show a readonly view |
172 abort(403) | 202 abort(403) |
173 else: | 203 else: |
174 return self.do_modify() | 204 return self.do_modify() |
175 | 205 |
176 def do_modify(self): | 206 def do_modify(self): |
177 is_new = isinstance(self.content, NonExistentContent) | 207 is_new = isinstance(self.content, NonExistentContent) |
178 closed = self.meta.get('closed') | 208 closed = self.meta.get('closed') |
179 | 209 |
180 Form = TicketSubmitForm if is_new else TicketUpdateForm | 210 Form = TicketSubmitForm if is_new else TicketUpdateForm |
181 | 211 |
182 if request.method in ['GET', 'HEAD']: | 212 if request.method in ['GET', 'HEAD']: |
183 form = Form.from_item(self) | 213 form = Form.from_item(self) |
184 elif request.method == 'POST': | 214 elif request.method == 'POST': |
185 form = Form.from_request(request) | 215 form = Form.from_request(request) |
186 if form.validate(): | 216 if form.validate(): |
187 meta, data = form._dump(self) | 217 meta, data, data_file = form._dump(self) |
188 try: | 218 try: |
189 self.modify(meta, data) | 219 self.modify(meta, data) |
220 file_upload(self, data_file) | |
190 except AccessDenied: | 221 except AccessDenied: |
191 abort(403) | 222 abort(403) |
192 else: | 223 else: |
193 try: | 224 try: |
194 fqname = CompositeName(self.meta[NAMESPACE], ITEMID, sel f.meta[ITEMID]) | 225 fqname = CompositeName(self.meta[NAMESPACE], ITEMID, sel f.meta[ITEMID]) |
195 except KeyError: | 226 except KeyError: |
196 fqname = self.fqname | 227 fqname = self.fqname |
197 return redirect(url_for('.show_item', item_name=fqname)) | 228 return redirect(url_for('.show_item', item_name=fqname)) |
198 | 229 |
199 # XXX When creating new item, suppress the "foo doesn't exist. Create it ?" dummy content | 230 # XXX When creating new item, suppress the "foo doesn't exist. Create it ?" dummy content |
200 data_rendered = None if is_new else Markup(self.content._render_data()) | 231 data_rendered = None if is_new else Markup(self.content._render_data()) |
201 | 232 |
233 files = get_files(self) | |
202 suggested_tags = get_itemtype_specific_tags(ITEMTYPE_TICKET) | 234 suggested_tags = get_itemtype_specific_tags(ITEMTYPE_TICKET) |
203 | 235 |
204 return render_template(self.submit_template if is_new else self.modify_t emplate, | 236 return render_template(self.submit_template if is_new else self.modify_t emplate, |
205 is_new=is_new, | 237 is_new=is_new, |
206 closed=closed, | 238 closed=closed, |
207 item_name=self.name, | 239 item_name=self.name, |
208 data_rendered=data_rendered, | 240 data_rendered=data_rendered, |
209 form=form, | 241 form=form, |
210 suggested_tags=suggested_tags, | 242 suggested_tags=suggested_tags, |
211 item=self, | 243 item=self, |
244 files=files, | |
212 ) | 245 ) |
OLD | NEW |