def request_embargo_termination(self, auth): """Initiates an EmbargoTerminationApproval to lift this Embargoed Registration's embargo early.""" if not self.is_embargoed: raise NodeStateError('This node is not under active embargo') if not self.root == self: raise NodeStateError( 'Only the root of an embargoed registration can request termination' ) approval = EmbargoTerminationApproval( initiated_by=auth.user, embargoed_registration=self, ) admins = [ admin for admin in self.root.get_admin_contributors_recursive( unique_users=True) ] for (admin, node) in admins: approval.add_authorizer(admin, node=node) approval.save() approval.ask(admins) self.embargo_termination_approval = approval self.save() return approval
def retract_registration(self, user, justification=None, save=True): """Retract public registration. Instantiate new Retraction object and associate it with the respective registration. """ if not self.is_public and not (self.embargo_end_date or self.is_pending_embargo): raise NodeStateError( 'Only public or embargoed registrations may be withdrawn.') if self.root_id != self.id: raise NodeStateError( 'Withdrawal of non-parent registrations is not permitted.') retraction = self._initiate_retraction(user, justification) self.registered_from.add_log( action=NodeLog.RETRACTION_INITIATED, params={ 'node': self.registered_from._id, 'registration': self._id, 'retraction_id': retraction._id, }, auth=Auth(user), ) self.retraction = retraction if save: self.save() return retraction
def set_editing(self, permissions, auth=None, log=False): """Set the editing permissions for this node. :param auth: All the auth information including user, API key :param bool permissions: True = publicly editable :param bool save: Whether to save the privacy change :param bool log: Whether to add a NodeLog for the privacy change if true the node object is also saved """ node = self.owner if permissions and not self.is_publicly_editable: if node.is_public: self.is_publicly_editable = True else: raise NodeStateError('Private components cannot be made publicly editable.') elif not permissions and self.is_publicly_editable: self.is_publicly_editable = False else: raise NodeStateError('Desired permission change is the same as current setting.') if log: node.add_log( action=(NodeLog.MADE_WIKI_PUBLIC if self.is_publicly_editable else NodeLog.MADE_WIKI_PRIVATE), params={ 'project': node.parent_id, 'node': node._primary_key, }, auth=auth, save=True, ) self.save()
def remove_node(self, auth, date=None): # QuickFilesNodes are only delete-able for disabled users # This is only done when doing a GDPR-delete if auth.user.is_disabled: super(QuickFilesNode, self).remove_node(auth=auth, date=date) else: raise NodeStateError('A QuickFilesNode may not be deleted.')
def new_bookmark_collection(user): """Create a new bookmark collection project. :param User user: User object :return Node: Created node """ existing_bookmark_collection = Node.find( Q('is_bookmark_collection', 'eq', True) & Q('contributors', 'eq', user._id) & Q('is_deleted', 'eq', False) ) if existing_bookmark_collection.count() > 0: raise NodeStateError('Users may only have one bookmark collection') node = Node( title='Bookmarks', creator=user, category='project', is_bookmark_collection=True, is_collection=True ) node.save() return node
def _on_complete(self, user): NodeLog = apps.get_model('osf.NodeLog') register = self._get_registration() if register.is_spammy: raise NodeStateError('Cannot approve a spammy registration') super(RegistrationApproval, self)._on_complete(user) self.state = Sanction.APPROVED self.save() registered_from = register.registered_from # Pass auth=None because the registration initiator may not be # an admin on components (component admins had the opportunity # to disapprove the registration by this point) register.set_privacy('public', auth=None, log=False) for child in register.get_descendants_recursive(primary_only=True): child.set_privacy('public', auth=None, log=False) # Accounts for system actions where no `User` performs the final approval auth = Auth(user) if user else None registered_from.add_log( action=NodeLog.REGISTRATION_APPROVAL_APPROVED, params={ 'node': registered_from._id, 'registration': register._id, 'registration_approval_id': self._id, }, auth=auth, ) for node in register.root.node_and_primary_descendants(): self._add_success_logs(node, user) node.update_search() # update search if public self.save()
def terminate_embargo(self, auth): """Handles the actual early termination of an Embargoed registration. Adds a log to the registered_from Node. """ if not self.is_embargoed: raise NodeStateError('This node is not under active embargo') self.registered_from.add_log( action=NodeLog.EMBARGO_TERMINATED, params={ 'project': self._id, 'node': self.registered_from._id, 'registration': self._id, }, auth=None, save=True ) self.embargo.mark_as_completed() for node in self.node_and_primary_descendants(): node.set_privacy( self.PUBLIC, auth=None, log=False, save=True ) return True
def add_node_link(self, node, auth, save=True): """Add a node link to a node. :param Node node: Node to add :param Auth auth: Consolidated authorization :param bool save: Save changes :return: Created pointer """ # Fail if node already in nodes / pointers. Note: cast node and node # to primary keys to test for conflicts with both nodes and pointers # contained in `self.nodes`. if NodeRelation.objects.filter(parent=self, child=node, is_node_link=True).exists(): raise ValueError('Link to node {0} already exists'.format( node._id)) if self.is_registration: raise NodeStateError('Cannot add a node link to a registration') # If a folder, prevent more than one pointer to that folder. # This will prevent infinite loops on the project organizer. if node.is_collection and node.linked_from.exists(): raise ValueError( 'Node link to folder {0} already exists. ' 'Only one node link to any given folder allowed'.format( node._id)) if node.is_collection and node.is_bookmark_collection: raise ValueError( 'Node link to bookmark collection ({0}) not allowed.'.format( node._id)) # Append node link node_relation, created = NodeRelation.objects.get_or_create( parent=self, child=node, is_node_link=True) # Add log if hasattr(self, 'add_log'): self.add_log( action=NodeLog.NODE_LINK_CREATED, params={ 'parent_node': self.parent_id, 'node': self._id, 'pointer': { 'id': node._id, 'url': node.url, 'title': node.title, 'category': node.category, }, }, auth=auth, save=False, ) # Optionally save changes if save: self.save() return node_relation
def delete(self): """ Mark collection as deleted """ if self.is_bookmark_collection: # Not really the right exception to raise, but it's for back-compatibility # TODO: Use a more correct exception and catch it in the necessary places raise NodeStateError('Bookmark collections may not be deleted.') self.deleted = timezone.now() self.save()
def create_for_user(self, user): possessive_title = get_quickfiles_project_title(user) quickfiles, created = QuickFilesNode.objects.get_or_create( title=possessive_title, creator=user) if not created: raise NodeStateError('Users may only have one quickfiles project') quickfiles.add_addon('osfstorage', auth=None, log=False) return quickfiles
def create_for_node(self, node, name, content, auth): existing_wiki_page = WikiPage.objects.get_for_node(node, name) if existing_wiki_page: raise NodeStateError('Wiki Page already exists.') wiki_page = WikiPage.objects.create( node=node, page_name=name, user=auth.user ) # Creates a WikiVersion object wiki_page.update(auth.user, content) return wiki_page
def delete(self): """ Mark collection as deleted """ if self.is_bookmark_collection: # Not really the right exception to raise, but it's for back-compatibility # TODO: Use a more correct exception and catch it in the necessary places raise NodeStateError('Bookmark collections may not be deleted.') self.deleted = timezone.now() if self.is_public: self.bulk_update_search(list(self.collectedguidmetadata_set.all()), op='delete') self.save()
def add_node_link(self, node, auth, save=True): """Add a node link to a node. :param Node node: Node to add :param Auth auth: Consolidated authorization :param bool save: Save changes :return: Created pointer """ # Fail if node already in nodes / pointers. Note: cast node and node # to primary keys to test for conflicts with both nodes and pointers # contained in `self.nodes`. if NodeRelation.objects.filter(parent=self, child=node, is_node_link=True).exists(): raise ValueError( 'Link to node {0} already exists'.format(node._id) ) if self.is_registration: raise NodeStateError('Cannot add a node link to a registration') # Append node link node_relation, created = NodeRelation.objects.get_or_create( parent=self, child=node, is_node_link=True ) # Add log if hasattr(self, 'add_log'): self.add_log( action=NodeLog.NODE_LINK_CREATED, params={ 'parent_node': self.parent_id, 'node': self._id, 'pointer': { 'id': node._id, 'url': node.url, 'title': node.title, 'category': node.category, }, }, auth=auth, save=False, ) # Optionally save changes if save: self.save() return node_relation
def _on_complete(self, user): NodeLog = apps.get_model('osf.NodeLog') parent_registration = self._get_registration() if parent_registration.is_spammy: raise NodeStateError('Cannot complete a spammy registration.') super(Embargo, self)._on_complete(user) parent_registration.registered_from.add_log( action=NodeLog.EMBARGO_APPROVED, params={ 'node': parent_registration.registered_from._id, 'registration': parent_registration._id, 'embargo_id': self._id, }, auth=Auth(self.initiated_by), ) self.save()
def new_bookmark_collection(user): """Create a new bookmark collection project. :param User user: User object :return Node: Created node """ Collection = apps.get_model('osf.Collection') existing_bookmark_collections = Collection.objects.filter( is_bookmark_collection=True, creator=user, is_deleted=False).exists() if existing_bookmark_collections: raise NodeStateError('Users may only have one bookmark collection') collection = Collection(title='Bookmarks', creator=user, is_bookmark_collection=True) collection.save() return collection
def new_bookmark_collection(user): """Create a new bookmark collection project. :param User user: User object :return Node: Created node """ Collection = apps.get_model('osf.Collection') existing_bookmark_collection = Collection.find( Q('is_bookmark_collection', 'eq', True) & Q('creator', 'eq', user) & Q('is_deleted', 'eq', False)) if existing_bookmark_collection.count() > 0: raise NodeStateError('Users may only have one bookmark collection') collection = Collection(title='Bookmarks', creator=user, is_bookmark_collection=True) collection.save() return collection
def set_subjects(self, new_subjects, auth, add_log=True): """ Helper for setting M2M subjects field from list of hierarchies received from UI. Only authorized admins may set subjects. :param list[list[Subject._id]] new_subjects: List of subject hierarchies to be validated and flattened :param Auth auth: Auth object for requesting user :param bool add_log: Whether or not to add a log (if called on a Loggable object) :return: None """ if getattr(self, 'is_registration', False): raise PermissionsError('Registrations may not be modified.') if getattr(self, 'is_collection', False): raise NodeStateError('Collections may not have subjects') if not self.has_permission(auth.user, ADMIN): raise PermissionsError('Only admins can change subjects.') old_subjects = list(self.subjects.values_list('id', flat=True)) self.subjects.clear() for subj_list in new_subjects: subj_hierarchy = [] for s in subj_list: subj_hierarchy.append(s) if subj_hierarchy: validate_subject_hierarchy(subj_hierarchy) for s_id in subj_hierarchy: self.subjects.add(Subject.load(s_id)) if add_log and hasattr(self, 'add_log'): self.add_log( action=NodeLog.SUBJECTS_UPDATED, params={ 'subjects': list(self.subjects.values('_id', 'text')), 'old_subjects': list(Subject.objects.filter(id__in=old_subjects).values('_id', 'text')) }, auth=auth, save=False, ) self.save(old_subjects=old_subjects)
def new_dashboard(user): """Create a new dashboard project. :param User user: User object :return Node: Created node """ existing_dashboards = user.node__contributed.find( Q('category', 'eq', 'project') & Q('is_dashboard', 'eq', True)) if existing_dashboards.count() > 0: raise NodeStateError("Users may only have one dashboard") node = Node(title='Dashboard', creator=user, category='project', is_dashboard=True, is_folder=True) node.save() return node
def add_addon(self, name, auth, log=True): if name != 'osfstorage': raise NodeStateError( 'A QuickFilesNode can only have the osfstorage addon.') return super(QuickFilesNode, self).add_addon(name, auth, log)
def set_privacy(self, permissions, *args, **kwargs): raise NodeStateError('You may not set privacy for a QuickFilesNode.')
def update_node_wiki(self, name, content, auth): raise NodeStateError('Registered wiki pages cannot be edited.')
def rename_node_wiki(self, name, new_name, auth): raise NodeStateError('Registered wiki pages cannot be renamed.')
def delete_node_wiki(self, name_or_page, auth): raise NodeStateError('Registered wiki pages cannot be deleted.')
def remove_tags(self, tags, auth, save=True): if self.retraction is None: super(Registration, self).remove_tags(tags, auth, save) else: raise NodeStateError( 'Cannot remove tags of withdrawn registrations.')
def add_tags(self, tags, auth=None, save=True, log=True, system=False): if self.retraction is None: super(Registration, self).add_tags(tags, auth, save, log, system) else: raise NodeStateError('Cannot add tags to withdrawn registrations.')
def remove_node(self, auth, date=None): raise NodeStateError('A QuickFilesNode may not be deleted.')
def clone(self): raise NodeStateError( 'A QuickFilesNode may not be forked, used as a template, or registered.' )
def add_contributor(self, contributor, *args, **kwargs): raise NodeStateError( 'A QuickFilesNode may not have additional contributors.')