|
1 # |
|
2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net> |
|
3 # All Rights Reserved. |
|
4 # |
|
5 # This software is subject to the provisions of the Zope Public License, |
|
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
|
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
|
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
|
10 # FOR A PARTICULAR PURPOSE. |
|
11 # |
|
12 |
|
13 __docformat__ = 'restructuredtext' |
|
14 |
|
15 |
|
16 # import standard library |
|
17 |
|
18 # import interfaces |
|
19 from pyams_skin.interfaces.viewlet import IToolbarViewletManager |
|
20 from pyams_skin.layer import IPyAMSLayer |
|
21 from pyams_thesaurus.interfaces.term import IThesaurusTerm |
|
22 from pyams_thesaurus.interfaces.thesaurus import IThesaurus |
|
23 from pyams_zmi.layer import IAdminLayer |
|
24 from z3c.form.interfaces import IDataExtractedEvent, DISPLAY_MODE |
|
25 |
|
26 # import packages |
|
27 from pyams_form.form import AJAXAddForm, AJAXEditForm |
|
28 from pyams_pagelet.pagelet import pagelet_config |
|
29 from pyams_skin.viewlet.toolbar import ToolbarAction |
|
30 from pyams_thesaurus.term import ThesaurusTerm |
|
31 from pyams_thesaurus.zmi.thesaurus import ThesaurusTermsView |
|
32 from pyams_utils.traversing import get_parent |
|
33 from pyams_utils.url import absolute_url |
|
34 from pyams_viewlet.viewlet import viewlet_config |
|
35 from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm |
|
36 from pyramid.events import subscriber |
|
37 from pyramid.view import view_config |
|
38 from z3c.form import field |
|
39 from zope.interface import Invalid |
|
40 |
|
41 from pyams_thesaurus import _ |
|
42 |
|
43 |
|
44 @viewlet_config(name='thesaurus.term.adding', context=IThesaurus, view=ThesaurusTermsView, |
|
45 layer=IAdminLayer, manager=IToolbarViewletManager, permission='thesaurus.manage') |
|
46 class ThesaurusTermAddAction(ToolbarAction): |
|
47 """Thesaurus term add action""" |
|
48 |
|
49 label = _("Add term") |
|
50 url = 'add-term.html' |
|
51 modal_target = True |
|
52 |
|
53 |
|
54 @pagelet_config(name='add-term.html', context=IThesaurus, layer=IPyAMSLayer, permission='thesaurus.manage') |
|
55 class ThesaurusTermAddForm(AdminDialogAddForm): |
|
56 """Thesaurus term add form""" |
|
57 |
|
58 @property |
|
59 def title(self): |
|
60 return self.context.title |
|
61 |
|
62 legend = _("Add new term") |
|
63 icon_css_class = 'fa fa-fw fa-tag' |
|
64 |
|
65 fields = field.Fields(IThesaurusTerm).select('label', 'alt', 'definition', 'note', 'generic', 'associations', |
|
66 'usage', 'extensions', 'status', 'created') |
|
67 |
|
68 ajax_handler = 'add-term.json' |
|
69 edit_permission = None |
|
70 |
|
71 def updateWidgets(self, prefix=None): |
|
72 super(ThesaurusTermAddForm, self).updateWidgets(prefix) |
|
73 for name in ('definition', 'note'): |
|
74 self.widgets[name].label_css_class = 'input textarea' |
|
75 for name in ('generic', 'associations', 'usage'): |
|
76 self.widgets[name].thesaurus_name = self.context.name |
|
77 |
|
78 def create(self, data): |
|
79 return ThesaurusTerm() |
|
80 |
|
81 def update_content(self, content, data): |
|
82 super(ThesaurusTermAddForm, self).update_content(content, data) |
|
83 generic = content.generic |
|
84 usage = content.usage |
|
85 if (generic is None) and (usage is None): |
|
86 IThesaurus(self.context).top_terms += [content, ] |
|
87 else: |
|
88 if generic is not None: |
|
89 generic.specifics += [content, ] |
|
90 elif usage is not None: |
|
91 usage.used_for += [content, ] |
|
92 |
|
93 def add(self, object): |
|
94 self.context.terms[object.label] = object |
|
95 |
|
96 def nextURL(self): |
|
97 return absolute_url(self.context, self.request, 'terms.html') |
|
98 |
|
99 |
|
100 @subscriber(IDataExtractedEvent, form_selector=ThesaurusTermAddForm) |
|
101 def handle_new_term_data_extraction(event): |
|
102 """Handle new term data extraction""" |
|
103 data = event.data |
|
104 if data.get('label') in event.form.context.terms: |
|
105 event.form.widgets.errors += (Invalid(_("Specified label is already used!")), ) |
|
106 |
|
107 |
|
108 @view_config(name='add-term.json', context=IThesaurus, request_type=IPyAMSLayer, |
|
109 permission='thesaurus.manage', renderer='json', xhr=True) |
|
110 class ThesaurusTermAJAXAddForm(AJAXAddForm, ThesaurusTermAddForm): |
|
111 """Thesaurus term add form, AJAX view""" |
|
112 |
|
113 def get_ajax_output(self, changes): |
|
114 if changes.generic is not None: |
|
115 return super(ThesaurusTermAJAXAddForm, self).get_ajax_output(changes) |
|
116 else: |
|
117 label = changes.label.replace("'", "'") |
|
118 return {'callback': 'PyAMS_thesaurus.tree.findTerm', |
|
119 'options': {'term': label}} |
|
120 |
|
121 |
|
122 @pagelet_config(name='properties.html', context=IThesaurusTerm, layer=IPyAMSLayer, permission='view') |
|
123 class ThesaurusTermEditForm(AdminDialogEditForm): |
|
124 """Thesaurus term edit form""" |
|
125 |
|
126 @property |
|
127 def title(self): |
|
128 return self.context.label |
|
129 |
|
130 legend = _("Edit term properties") |
|
131 icon_css_class = 'fa fa-fw fa-tag' |
|
132 |
|
133 fields = field.Fields(IThesaurusTerm).select('label', 'alt', 'definition', 'note', 'generic', 'specifics', |
|
134 'associations', 'usage', 'used_for', 'extracts', 'extensions', |
|
135 'status', 'level', 'created', 'modified') |
|
136 |
|
137 ajax_handler = 'properties.json' |
|
138 edit_permission = 'thesaurus.manage' |
|
139 |
|
140 generic_changed = False |
|
141 usage_changed = False |
|
142 |
|
143 def updateWidgets(self, prefix=None): |
|
144 super(ThesaurusTermEditForm, self).updateWidgets(prefix) |
|
145 thesaurus = get_parent(self.context, IThesaurus) |
|
146 for name in ('definition', 'note'): |
|
147 self.widgets[name].label_css_class = 'input textarea' |
|
148 for name in ('generic', 'specifics', 'associations', 'usage', 'used_for'): |
|
149 self.widgets[name].thesaurus_name = thesaurus.name |
|
150 for name in ('specifics', 'used_for', 'extracts', 'created'): |
|
151 self.widgets[name].mode = DISPLAY_MODE |
|
152 |
|
153 def update_content(self, content, data): |
|
154 term = self.context |
|
155 thesaurus = get_parent(term, IThesaurus) |
|
156 old_label = term.label |
|
157 old_generic = term.generic |
|
158 old_usage = term.usage |
|
159 changes = super(ThesaurusTermEditForm, self).update_content(content, data) |
|
160 # Move term if label changed |
|
161 if 'label' in changes.get(IThesaurusTerm, ()): |
|
162 terms = thesaurus.terms |
|
163 del terms[old_label] |
|
164 terms[term.label] = term |
|
165 # Check modifications |
|
166 self.generic_changed = old_generic != term.generic |
|
167 self.usage_changed = old_usage != term.usage |
|
168 # Check generic change |
|
169 if self.generic_changed: |
|
170 if old_generic is not None: |
|
171 # Add a previous generic? |
|
172 # => remove term from list of previous term's specifics |
|
173 specifics = old_generic.specifics |
|
174 if term in specifics: |
|
175 specifics.remove(term) |
|
176 old_generic.specifics = specifics |
|
177 else: |
|
178 # No previous generic? |
|
179 # => remove term from thesaurus top terms |
|
180 top_terms = thesaurus.top_terms |
|
181 if term in top_terms: |
|
182 top_terms.remove(term) |
|
183 thesaurus.top_terms = top_terms |
|
184 # Check new value |
|
185 if term.generic is None: |
|
186 # No generic and not a synonym? |
|
187 # => add term to top terms |
|
188 if (term.usage is None) and (term not in thesaurus.top_terms): |
|
189 thesaurus.top_terms += [term, ] |
|
190 else: |
|
191 # New generic? |
|
192 # => add term to generic specific terms |
|
193 if term not in term.generic.specifics: |
|
194 term.generic.specifics += [term, ] |
|
195 # Check usage change |
|
196 if self.usage_changed: |
|
197 if old_usage is not None: |
|
198 # Add previous usage term? |
|
199 # => update used_for |
|
200 used_for = old_usage.used_for |
|
201 if term in used_for: |
|
202 used_for.remove(term) |
|
203 old_usage.used_for = used_for |
|
204 # Check new term usage |
|
205 if term.usage is None: |
|
206 # No usage |
|
207 # => maybe a top term... |
|
208 if (term.generic is None) and (term not in thesaurus.top_terms): |
|
209 thesaurus.top_terms += [term, ] |
|
210 else: |
|
211 # Term usage? |
|
212 # => remove term from top terms |
|
213 top_terms = thesaurus.top_terms |
|
214 if term in top_terms: |
|
215 top_terms.remove(term) |
|
216 thesaurus.top_terms = top_terms |
|
217 # Add term to usage synonyms |
|
218 if term not in term.usage.used_for: |
|
219 term.usage.used_for += [term, ] |
|
220 return changes |
|
221 |
|
222 |
|
223 @subscriber(IDataExtractedEvent, form_selector=ThesaurusTermEditForm) |
|
224 def handle_term_properties_data_extraction(event): |
|
225 """Handle term properties data extraction""" |
|
226 context = event.form.context |
|
227 thesaurus = get_parent(context, IThesaurus) |
|
228 label = event.data.get('label') |
|
229 if (label != context.label) and (label in thesaurus.terms): |
|
230 event.form.widgets.errors += (Invalid(_("Specified new label is already used!")), ) |
|
231 |
|
232 |
|
233 @pagelet_config(name='properties.json', context=IThesaurusTerm, request_type=IPyAMSLayer, |
|
234 permission='thesaurus.manage', renderer='json', xhr=True) |
|
235 class ThesaurusTermAJAXEditForm(AJAXEditForm, ThesaurusTermEditForm): |
|
236 """Thesaurus term edit form, AJAX view""" |
|
237 |
|
238 def get_ajax_output(self, changes): |
|
239 if self.generic_changed: |
|
240 label = self.context.label.replace("'", "'") |
|
241 return {'status': 'reload', |
|
242 'callback': 'PyAMS_thesaurus.tree.findTerm', |
|
243 'options': {'term': label}} |
|
244 else: |
|
245 term_changes = changes.get(IThesaurusTerm, ()) |
|
246 if ('status' in term_changes) or ('label' in term_changes): |
|
247 label = self.context.generic.label.replace("'", "'") |
|
248 return {'status': 'callback', |
|
249 'callback': 'PyAMS_thesaurus.tree.updateTerm', |
|
250 'options': {'term': label}} |
|
251 else: |
|
252 return super(ThesaurusTermAJAXEditForm, self).get_ajax_output(changes) |