62 if 'uniquecstrholder' in session.transaction_data: |
62 if 'uniquecstrholder' in session.transaction_data: |
63 del session.transaction_data['uniquecstrholder'] |
63 del session.transaction_data['uniquecstrholder'] |
64 _UNIQUE_CONSTRAINTS_LOCK.release() |
64 _UNIQUE_CONSTRAINTS_LOCK.release() |
65 |
65 |
66 class _ReleaseUniqueConstraintsOperation(hook.Operation): |
66 class _ReleaseUniqueConstraintsOperation(hook.Operation): |
67 def commit_event(self): |
|
68 pass |
|
69 def postcommit_event(self): |
67 def postcommit_event(self): |
70 _release_unique_cstr_lock(self.session) |
68 _release_unique_cstr_lock(self.session) |
71 def rollback_event(self): |
69 def rollback_event(self): |
72 _release_unique_cstr_lock(self.session) |
70 _release_unique_cstr_lock(self.session) |
73 |
71 |
183 constraint.repo_check(session, eidfrom, rtype, eidto) |
181 constraint.repo_check(session, eidfrom, rtype, eidto) |
184 except NotImplementedError: |
182 except NotImplementedError: |
185 self.critical('can\'t check constraint %s, not supported', |
183 self.critical('can\'t check constraint %s, not supported', |
186 constraint) |
184 constraint) |
187 |
185 |
188 def commit_event(self): |
|
189 pass |
|
190 |
|
191 |
186 |
192 class CheckConstraintHook(IntegrityHook): |
187 class CheckConstraintHook(IntegrityHook): |
193 """check the relation satisfy its constraints |
188 """check the relation satisfy its constraints |
194 |
189 |
195 this is delayed to a precommit time operation since other relation which |
190 this is delayed to a precommit time operation since other relation which |
217 __regid__ = 'checkattrconstraint' |
212 __regid__ = 'checkattrconstraint' |
218 events = ('after_add_entity', 'after_update_entity') |
213 events = ('after_add_entity', 'after_update_entity') |
219 |
214 |
220 def __call__(self): |
215 def __call__(self): |
221 eschema = self.entity.e_schema |
216 eschema = self.entity.e_schema |
222 for attr in self.entity.edited_attributes: |
217 for attr in self.entity.cw_edited: |
223 if eschema.subjrels[attr].final: |
218 if eschema.subjrels[attr].final: |
224 constraints = [c for c in eschema.rdef(attr).constraints |
219 constraints = [c for c in eschema.rdef(attr).constraints |
225 if isinstance(c, (RQLUniqueConstraint, RQLConstraint))] |
220 if isinstance(c, (RQLUniqueConstraint, RQLConstraint))] |
226 if constraints: |
221 if constraints: |
227 hook.set_operation(self._cw, 'check_constraints_op', |
222 hook.set_operation(self._cw, 'check_constraints_op', |
234 events = ('before_add_entity', 'before_update_entity') |
229 events = ('before_add_entity', 'before_update_entity') |
235 |
230 |
236 def __call__(self): |
231 def __call__(self): |
237 entity = self.entity |
232 entity = self.entity |
238 eschema = entity.e_schema |
233 eschema = entity.e_schema |
239 for attr in entity.edited_attributes: |
234 for attr, val in entity.cw_edited.iteritems(): |
240 if eschema.subjrels[attr].final and eschema.has_unique_values(attr): |
235 if eschema.subjrels[attr].final and eschema.has_unique_values(attr): |
241 val = entity[attr] |
|
242 if val is None: |
236 if val is None: |
243 continue |
237 continue |
244 rql = '%s X WHERE X %s %%(val)s' % (entity.e_schema, attr) |
238 rql = '%s X WHERE X %s %%(val)s' % (entity.e_schema, attr) |
245 rset = self._cw.execute(rql, {'val': val}) |
239 rset = self._cw.execute(rql, {'val': val}) |
246 if rset and rset[0][0] != entity.eid: |
240 if rset and rset[0][0] != entity.eid: |
255 __regid__ = 'checkownersgroup' |
249 __regid__ = 'checkownersgroup' |
256 __select__ = IntegrityHook.__select__ & is_instance('CWGroup') |
250 __select__ = IntegrityHook.__select__ & is_instance('CWGroup') |
257 events = ('before_delete_entity', 'before_update_entity') |
251 events = ('before_delete_entity', 'before_update_entity') |
258 |
252 |
259 def __call__(self): |
253 def __call__(self): |
260 if self.event == 'before_delete_entity' and self.entity.name == 'owners': |
254 entity = self.entity |
|
255 if self.event == 'before_delete_entity' and entity.name == 'owners': |
261 msg = self._cw._('can\'t be deleted') |
256 msg = self._cw._('can\'t be deleted') |
262 raise ValidationError(self.entity.eid, {None: msg}) |
257 raise ValidationError(entity.eid, {None: msg}) |
263 elif self.event == 'before_update_entity' and \ |
258 elif self.event == 'before_update_entity' \ |
264 'name' in self.entity.edited_attributes: |
259 and 'name' in entity.cw_edited: |
265 newname = self.entity.pop('name') |
260 oldname, newname = entity.cw_edited.oldnewvalue('name') |
266 oldname = self.entity.name |
|
267 if oldname == 'owners' and newname != oldname: |
261 if oldname == 'owners' and newname != oldname: |
268 qname = role_name('name', 'subject') |
262 qname = role_name('name', 'subject') |
269 msg = self._cw._('can\'t be changed') |
263 msg = self._cw._('can\'t be changed') |
270 raise ValidationError(self.entity.eid, {qname: msg}) |
264 raise ValidationError(entity.eid, {qname: msg}) |
271 self.entity['name'] = newname |
|
272 |
265 |
273 |
266 |
274 class TidyHtmlFields(IntegrityHook): |
267 class TidyHtmlFields(IntegrityHook): |
275 """tidy HTML in rich text strings""" |
268 """tidy HTML in rich text strings""" |
276 __regid__ = 'htmltidy' |
269 __regid__ = 'htmltidy' |
277 events = ('before_add_entity', 'before_update_entity') |
270 events = ('before_add_entity', 'before_update_entity') |
278 |
271 |
279 def __call__(self): |
272 def __call__(self): |
280 entity = self.entity |
273 entity = self.entity |
281 metaattrs = entity.e_schema.meta_attributes() |
274 metaattrs = entity.e_schema.meta_attributes() |
|
275 edited = entity.cw_edited |
282 for metaattr, (metadata, attr) in metaattrs.iteritems(): |
276 for metaattr, (metadata, attr) in metaattrs.iteritems(): |
283 if metadata == 'format' and attr in entity.edited_attributes: |
277 if metadata == 'format' and attr in edited: |
284 try: |
278 try: |
285 value = entity[attr] |
279 value = edited[attr] |
286 except KeyError: |
280 except KeyError: |
287 continue # no text to tidy |
281 continue # no text to tidy |
288 if isinstance(value, unicode): # filter out None and Binary |
282 if isinstance(value, unicode): # filter out None and Binary |
289 if getattr(entity, str(metaattr)) == 'text/html': |
283 if getattr(entity, str(metaattr)) == 'text/html': |
290 entity[attr] = soup2xhtml(value, self._cw.encoding) |
284 edited[attr] = soup2xhtml(value, self._cw.encoding) |
291 |
285 |
292 |
286 |
293 class StripCWUserLoginHook(IntegrityHook): |
287 class StripCWUserLoginHook(IntegrityHook): |
294 """ensure user logins are stripped""" |
288 """ensure user logins are stripped""" |
295 __regid__ = 'stripuserlogin' |
289 __regid__ = 'stripuserlogin' |
296 __select__ = IntegrityHook.__select__ & is_instance('CWUser') |
290 __select__ = IntegrityHook.__select__ & is_instance('CWUser') |
297 events = ('before_add_entity', 'before_update_entity',) |
291 events = ('before_add_entity', 'before_update_entity',) |
298 |
292 |
299 def __call__(self): |
293 def __call__(self): |
300 user = self.entity |
294 login = self.entity.cw_edited.get('login') |
301 if 'login' in user.edited_attributes and user.login: |
295 if login: |
302 user.login = user.login.strip() |
296 self.entity.cw_edited['login'] = login.strip() |
303 |
297 |
304 |
298 |
305 # 'active' integrity hooks: you usually don't want to deactivate them, they are |
299 # 'active' integrity hooks: you usually don't want to deactivate them, they are |
306 # not really integrity check, they maintain consistency on changes |
300 # not really integrity check, they maintain consistency on changes |
307 |
301 |