forked from hforge/ikaaro
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathroot_views.py
327 lines (261 loc) · 10.5 KB
/
root_views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# -*- coding: UTF-8 -*-
# Copyright (C) 2005-2008 Juan David Ibáñez Palomar <[email protected]>
# Copyright (C) 2007 Henry Obein <[email protected]>
# Copyright (C) 2007 Hervé Cauwelier <[email protected]>
# Copyright (C) 2007-2008 Sylvain Taverne <[email protected]>
# Copyright (C) 2008 Nicolas Deram <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Import from the Standard Library
from getpass import getuser
from json import dumps
from socket import gethostname
# Import from itools
from itools.core import get_abspath
from itools.datatypes import Email, String, Unicode, Integer
from itools.datatypes import Enumerate
from itools.fs import lfs, FileName
from itools.gettext import MSG
from itools.handlers import get_handler_class_by_mimetype
from itools.html import XHTMLFile
from itools.stl import rewrite_uris
from itools.uri import get_reference
from itools.web import BaseView, FormError, STLView, INFO
from itools.xml import get_element, TEXT
# Import from ikaaro
from autoform import AutoForm, FileWidget
from autoform import HiddenWidget, SelectWidget, MultilineWidget, TextWidget
from buttons import Button
from config_captcha import CaptchaDatatype, CaptchaWidget
from datatypes import FileDataType
from ikaaro.folder import Folder
from messages import MSG_UNEXPECTED_MIMETYPE
class NotFoundView(STLView):
template = '/ui/root/not_found.xml'
def get_namespace(self, resource, context):
return {'uri': str(context.uri)}
class ForbiddenView(STLView):
template = '/ui/root/forbidden.xml'
def POST(self, resource, context):
return self.GET
class UploadStatsView(BaseView):
access = True
query_schema = {'upload_id': Integer}
def GET(self, resource, context):
context.content_type = 'text/plain'
upload_id = context.query.get('upload_id')
if upload_id is None:
return dumps({'valid_id': False})
stats = context.server.upload_stats.get(upload_id)
if stats is None:
return dumps({'valid_id': False})
uploaded_size, total_size = stats
return dumps({'valid_id': True,
'uploaded_size': uploaded_size,
'total_size': total_size})
class ContactOptions(Enumerate):
def get_options(cls):
resource = cls.resource
users = resource.get_resource('/users')
mail = resource.get_resource('/config/mail')
options = []
for name in mail.get_value('contacts'):
user = users.get_resource(name, soft=True)
if user is None:
continue
title = user.get_title()
options.append({'name': name, 'value': title,
'sort_value': title.lower()})
options.sort(key=lambda x: x['sort_value'])
return options
class ContactForm(AutoForm):
access = True
title = MSG(u'Contact')
actions = [Button(access=True, css='button-ok', title=MSG(u'Send'))]
query_schema = {'to': String,
'subject': Unicode,
'message_body': Unicode}
def get_schema(self, resource, context):
to = ContactOptions(resource=resource, mandatory=True)
if len(to.get_options()) == 1:
to = String(mandatory=True)
return {
'to': to,
'from': Email(mandatory=True),
'subject': Unicode(mandatory=True),
'message_body': Unicode(mandatory=True),
'captcha': CaptchaDatatype}
def get_widgets(self, resource, context):
if len(ContactOptions(resource=resource).get_options()) == 1:
to = HiddenWidget('to')
else:
to = SelectWidget('to', title=MSG(u'Recipient'))
return [
to,
TextWidget('from', title=MSG(u'Your email address'), size=40),
TextWidget('subject', title=MSG(u'Message subject'), size=40),
MultilineWidget('message_body', title=MSG(u'Message body'),
rows=8, cols=50),
CaptchaWidget('captcha')]
def get_value(self, resource, context, name, datatype):
if name == 'to':
options = ContactOptions(resource=resource).get_options()
if len(options) == 1:
return options[0]['name']
if name == 'from':
user = context.user
if user is not None:
return user.get_value('email')
return datatype.get_default()
query = context.query
if name in query:
return query[name]
return datatype.get_default()
def action(self, resource, context, form):
# Get form values
contact = form['to']
reply_to = form['from'].strip()
subject = form['subject'].strip()
body = form['message_body'].strip()
# Find out the "to" address
contact = resource.get_resource('/users/%s' % contact)
contact_title = contact.get_title()
contact = contact.get_value('email')
if contact_title != contact:
contact = (contact_title, contact)
# Send the email
root = resource.get_root()
root.send_email(contact, subject, reply_to=reply_to, text=body)
# Ok
context.message = INFO(u'Message sent.')
class PoweredBy(STLView):
access = True
title = MSG(u'Powered by')
template = '/ui/root/powered-by.xml'
def get_namespace(self, resource, context):
namespace = {}
# Credits
credits = get_abspath('CREDITS.txt')
lines = lfs.open(credits).readlines()
names = [ x[2:].strip() for x in lines if x.startswith(' ') ]
namespace['hackers'] = names
# Installed software
root = context.root
is_admin = root.is_admin(context.user, resource)
namespace['is_admin'] = is_admin
if is_admin:
package2title = {
'gio': u'pygobject',
'lpod': u'lpOD',
'sys': u'Python',
'os': MSG(u'Operating System')}
packages = [
{'name': package2title.get(x, x),
'version': y or MSG('no version found')}
for x, y in root.get_version_of_packages(context).items() ]
location = (getuser(), gethostname(), context.server.target)
namespace['packages'] = packages
namespace['location'] = u'%s@%s:%s' % location
# Ok
return namespace
class UpdateDocs(AutoForm):
access = 'is_admin'
title = MSG(u'Update docs')
schema = {
'file': FileDataType(mandatory=True),
'language': String(mandatory=True, default='en')}
widgets = [
FileWidget('file'),
TextWidget('language', title=MSG(u'Language'),
tip=MSG(u'"en", "fr", ...'))]
actions = [
Button(access='is_admin', css='button-ok', title=MSG(u'Upload'))]
def _get_form(self, resource, context):
form = super(UpdateDocs, self)._get_form(resource, context)
# Check the mimetype
filename, mimetype, body = form['file']
if mimetype not in ('application/x-tar', 'application/zip'):
raise FormError, MSG_UNEXPECTED_MIMETYPE(mimetype=mimetype)
return form
def action(self, resource, context, form):
skip = set(['application/javascript', 'application/octet-stream',
'text/css', 'text/plain'])
keep = set(['application/pdf', 'image/png'])
language = form['language']
def rewrite(value):
if value[0] == '#':
return value
ref = get_reference(value)
if ref.scheme:
return value
name = ref.path.get_name()
name, extension, langage = FileName.decode(name)
if extension in ('png', 'pdf'):
name = '%s/;download' % name
ref.path[-1] = name
return '../%s' % ref
def filter(path, mimetype, body):
# HTML
if mimetype == 'text/html':
source = XHTMLFile(string=body)
target = XHTMLFile()
elem = get_element(source.events, 'div', **{'class': 'body'})
if not elem:
print 'E', path
return None
elements = elem.get_content_elements()
elements = rewrite_uris(elements, rewrite)
elements = list(elements)
target.set_body(elements)
return target.to_str()
# Skip
elif mimetype in skip:
return None
# Keep
elif mimetype in keep:
return body
# Unknown
else:
print 'X', path, mimetype
return body
def postproc(file):
# Share
file.set_value('share', ['everybody'])
# Title
if file.class_id != 'webpage':
return
handler = file.get_handler()
events = handler.events
elem = get_element(events, 'h1')
if elem:
title = [
unicode(x[1], 'utf8')
for x in elem.get_content_elements() if x[0] == TEXT ]
if title[-1] == u'¶':
title.pop()
title = u''.join(title)
file.set_property('title', title, language)
handler.events = events[:elem.start] + events[elem.end+1:]
# 1. Make the '/docs/' folder
docs = resource.get_resource('docs', soft=True)
if not docs:
docs = resource.make_resource('docs', Folder)
# 2. Extract
filename, mimetype, body = form['file']
cls = get_handler_class_by_mimetype(mimetype)
handler = cls(string=body)
docs.extract_archive(handler, language, filter, postproc, True)
# Ok
message = MSG(u'Documentation updated.')
return context.come_back(message, goto='./docs')