77
78
ctx['semesters'].append((semester, offerings))
81
class SubjectShortNameUniquenessValidator(formencode.FancyValidator):
82
"""A FormEncode validator that checks that a subject name is unused.
84
The subject referenced by state.existing_subject is permitted
85
to hold that name. If any other object holds it, the input is rejected.
87
def __init__(self, matching=None):
88
self.matching = matching
90
def _to_python(self, value, state):
92
Subject, short_name=value).one() not in
93
(None, state.existing_subject)):
94
raise formencode.Invalid(
95
'Short name already taken', value, state)
99
class SubjectSchema(formencode.Schema):
100
short_name = formencode.All(
101
SubjectShortNameUniquenessValidator(),
102
formencode.validators.UnicodeString(not_empty=True))
103
name = formencode.validators.UnicodeString(not_empty=True)
104
code = formencode.validators.UnicodeString(not_empty=True)
107
class SubjectFormView(XHTMLView):
108
"""An abstract form to add or edit a subject."""
111
def authorize(self, req):
112
return req.user is not None and req.user.admin
114
def filter(self, stream, ctx):
115
return stream | HTMLFormFiller(data=ctx['data'])
117
def populate_state(self, state):
118
state.existing_subject = None
120
def populate(self, req, ctx):
121
if req.method == 'POST':
122
data = dict(req.get_fieldstorage())
124
validator = SubjectSchema()
125
self.populate_state(req)
126
data = validator.to_python(data, state=req)
128
subject = self.update_subject_object(req, data)
131
req.throw_redirect(req.publisher.generate(subject))
132
except formencode.Invalid, e:
133
errors = e.unpack_errors()
135
data = self.get_default_data(req)
141
ctx['context'] = self.context
142
ctx['data'] = data or {}
143
ctx['errors'] = errors
146
class SubjectNew(SubjectFormView):
147
"""A form to create a subject."""
148
template = 'templates/subject-new.html'
150
def populate_state(self, state):
151
state.existing_subject = self.context
153
def get_default_data(self, req):
156
def update_subject_object(self, req, data):
157
new_subject = Subject()
158
new_subject.short_name = data['short_name']
159
new_subject.name = data['name']
160
new_subject.code = data['code']
162
req.store.add(new_subject)
166
class SubjectEdit(SubjectFormView):
167
"""A form to edit a subject."""
168
template = 'templates/subject-edit.html'
170
def populate_state(self, state):
171
state.existing_subject = self.context
173
def get_default_data(self, req):
175
'short_name': self.context.short_name,
176
'name': self.context.name,
177
'code': self.context.code,
180
def update_subject_object(self, req, data):
181
self.context.short_name = data['short_name']
182
self.context.name = data['name']
183
self.context.code = data['code']
79
188
class OfferingView(XHTMLView):
80
189
"""The home page of an offering."""
81
190
template = 'templates/offering.html'
327
436
reverse_routes = (subject_url, offering_url, projectset_url, project_url)
329
438
views = [(ApplicationRoot, ('subjects', '+index'), SubjectsView),
439
(ApplicationRoot, ('subjects', '+new'), SubjectNew),
440
(Subject, '+edit', SubjectEdit),
330
441
(Offering, '+index', OfferingView),
331
442
(Offering, '+edit', OfferingEdit),
332
443
(Offering, ('+enrolments', '+index'), EnrolmentsView),