def delete(self): """Deletes the snippet from the collection.""" # do not allow deletion of top-level aliases if self.collection.publish_state == PublicationState.PUBLISHED: raise NotAllowed( "You cannot delete a top-level snippet from a published collection." ) # clear all bindings self.collection.sub_coll(current_app.mdb).update_many( { "type": { "$in": ["subscribe", "server_active"] }, "object_id": self.collection.id }, {"$pull": { "snippet_bindings": { "id": self.id } }} # pull from the snippet_bindings array all docs with this id ) # remove reference from collection current_app.mdb.workshop_collections.update_one( {"_id": self.collection.id}, {"$pull": { "snippet_ids": self.id }}) self.collection.snippet_ids.remove(self.id) self.collection.update_edit_time() # delete from db self.mdb_coll().delete_one({"_id": self.id})
def set_active_code_version(self, version: int): """Sets the code version with version=version active.""" cv = next((cv for cv in self.versions if cv.version == version), None) if cv is None: raise NotAllowed("This code version does not exist") # set correct current version and update code self.mdb_coll().update_one({"_id": self.id}, { "$set": { "code": cv.content, "versions.$[current].is_current": True, "versions.$[notcurrent].is_current": False } }, array_filters=[{ "current.version": version }, { "notcurrent.version": { "$ne": version } }]) for old_cv in self.versions: old_cv.is_current = False cv.is_current = True self.code = cv.content self.collection.update_edit_time()
def create_new(cls, user_id: int, name, description, image): """Inserts a new collection into the database and returns the new collection.""" if not name: raise NotAllowed("Name is required.") if not description: raise NotAllowed("Description is required.") now = datetime.datetime.now() # noinspection PyTypeChecker # id is None until inserted inst = cls(None, name, description, image, user_id, [], [], PublicationState.PRIVATE, 0, 0, now, now, []) result = current_app.mdb.workshop_collections.insert_one( inst.to_dict()) inst.id = result.inserted_id inst.update_elasticsearch() return inst
def subscribe(self, user_id: int): """Adds the user as a subscriber.""" if self.is_subscribed(user_id): raise NotAllowed("You are already subscribed to this.") self.sub_coll(current_app.mdb).insert_one( {"type": "subscribe", "subscriber_id": user_id, "object_id": self.id} )
def set_state(self, new_state, run_checks=True): """ Updates the collection's publication state, running checks as necessary. :type new_state: str or PublicationState :param bool run_checks: Whether or not to run the publication state checks (bypass for moderation). """ if isinstance(new_state, str): new_state = PublicationState(new_state.upper()) if new_state == self.publish_state: # we don't need to do anything return if run_checks: # cannot unpublish if self.publish_state == PublicationState.PUBLISHED: raise NotAllowed( "You cannot unpublish a collection after it has been published" ) # prepublish check: name and description are present, at least one alias/snippet if new_state == PublicationState.PUBLISHED: if not self.name: raise NotAllowed( "A name must be present to publish this collection") if not self.description: raise NotAllowed( "A description must be present to publish this collection" ) if len(self.alias_ids) == 0 and len(self.snippet_ids) == 0: raise NotAllowed( "At least one alias or snippet must be present to publish this collection" ) current_app.mdb.workshop_collections.update_one({"_id": self.id}, { "$set": { "publish_state": new_state.value }, "$currentDate": { "last_edited": True } }) self.publish_state = new_state self.last_edited = datetime.datetime.now() self.update_elasticsearch()
def unsubscribe(self, user_id: int): """Removes the user from subscribers.""" if not self.is_subscribed(user_id): raise NotAllowed("You are not subscribed to this.") self.sub_coll(current_app.mdb).delete_many( {"type": {"$in": ["subscribe", "active"]}, "subscriber_id": user_id, "object_id": self.id} # unsubscribe, unactive )
def set_server_active(self, guild_id: int, alias_bindings=None, snippet_bindings=None, invoker_id: int = None): """Sets the object as active for the contextual guild, with given name bindings.""" if self.publish_state == PublicationState.PRIVATE \ and not (self.is_owner(invoker_id) or self.is_editor(invoker_id)): raise NotAllowed("This collection is private.") # generate default bindings if alias_bindings is None: alias_bindings = self._generate_default_alias_bindings() else: alias_bindings = self._bindings_sanity_check( self.alias_ids, alias_bindings, WorkshopAlias) if snippet_bindings is None: snippet_bindings = self._generate_default_snippet_bindings() else: snippet_bindings = self._bindings_sanity_check( self.snippet_ids, snippet_bindings, WorkshopSnippet) # insert sub doc result = self.sub_coll(current_app.mdb).update_one( { "type": "server_active", "subscriber_id": guild_id, "object_id": self.id }, { "$set": { "alias_bindings": alias_bindings, "snippet_bindings": snippet_bindings } }, upsert=True) if result.upserted_id is not None: # incr sub count current_app.mdb.workshop_collections.update_one( {"_id": self.id}, {"$inc": { "num_guild_subscribers": 1 }}) # log sub event self.log_event({ "type": "server_subscribe", "object_id": self.id, "timestamp": datetime.datetime.utcnow(), "user_id": invoker_id }) return { "alias_bindings": alias_bindings, "snippet_bindings": snippet_bindings, "new_subscription": result.upserted_id is not None }
def update_info(self, name: str, description: str, image): """Updates the collection's user information.""" if not name: raise NotAllowed("Name is required.") if not description: raise NotAllowed("Description is required.") current_app.mdb.workshop_collections.update_one({"_id": self.id}, { "$set": { "name": name, "description": description, "image": image }, "$currentDate": { "last_edited": True } }) self.name = name self.description = description self.image = image self.last_edited = datetime.datetime.now() self.update_elasticsearch()
def _bindings_check(coll, bindings): if bindings is None: return for binding in bindings: if not isinstance(binding, dict): raise NotAllowed("bindings must be list of {name, id}") if set(binding) != {"name", "id"}: raise NotAllowed("bindings must be list of {name, id}") if not isinstance(binding['name'], str): raise NotAllowed("binding name must be str") if isinstance(binding['id'], dict): if '$oid' not in binding['id']: raise NotAllowed("binding id must be ObjectId") oid = ObjectId(binding['id']['$oid']) elif isinstance(binding['id'], str): oid = ObjectId(binding['id']) else: raise NotAllowed("binding id must be ObjectId") if not (oid in coll.alias_ids or oid in coll.snippet_ids): raise NotAllowed("binding must be to object in collection") binding['id'] = oid
def add_entitlement(self, sourced_entity, required=False): """ Adds a required entitlement to this collectable. :type sourced_entity: gamedata.shared.Sourced :param bool required: Whether or not this entitlement is required by a moderator (cannot be removed). """ if sourced_entity.is_free: raise NotAllowed("This entitlement is for a free object.") re = RequiredEntitlement(sourced_entity.entity_type, sourced_entity.entity_id, required) if (re.entity_type, re.entity_id) in ((existing.entity_type, existing.entity_id) for existing in self.entitlements): raise NotAllowed( "This collectable already has this entitlement required.") # add to database self.mdb_coll().update_one({"_id": self.id}, {"$push": { "entitlements": re.to_dict() }}) self.collection.update_edit_time() self.entitlements.append(re) return [e.to_dict() for e in self.entitlements]
def delete(self, run_checks=True): """Deletes the alias from the collection.""" if run_checks: # do not allow deletion of top-level aliases if self.collection.publish_state == PublicationState.PUBLISHED and self._parent_id is None: raise NotAllowed( "You cannot delete a top-level alias from a published collection." ) if self._parent_id is None: # clear all bindings self.collection.sub_coll(current_app.mdb).update_many( { "type": { "$in": ["subscribe", "server_active"] }, "object_id": self.collection.id }, {"$pull": { "alias_bindings": { "id": self.id } }} # pull from the alias_bindings array all docs with this id ) # remove reference from collection current_app.mdb.workshop_collections.update_one( {"_id": self.collection.id}, {"$pull": { "alias_ids": self.id }}) self.collection.alias_ids.remove(self.id) else: # remove reference from parent self.mdb_coll().update_one({"_id": self.parent.id}, {"$pull": { "subcommand_ids": self.id }}) self.parent._subcommand_ids.remove(self.id) # delete all children for child in self.subcommands: child.delete(run_checks) self.collection.update_edit_time() # delete from db self.mdb_coll().delete_one({"_id": self.id})
def add_tag(self, tag: str): """Adds a tag to this collection. Validates the tag. Does nothing if the tag already exists.""" if current_app.mdb.workshop_tags.find_one({"slug": tag}) is None: raise NotAllowed(f"{tag} is not a valid tag") if tag in self.tags: return # we already have the tag, do a no-op current_app.mdb.workshop_collections.update_one({"_id": self.id}, { "$push": { "tags": tag }, "$currentDate": { "last_edited": True } }) self.tags.append(tag) self.last_edited = datetime.datetime.now() self.update_elasticsearch()
def delete(self): # do not allow deletion of published collections if self.publish_state == PublicationState.PUBLISHED: raise NotAllowed("You cannot delete a published collection.") # delete all children for alias in self.aliases: alias.delete() for snippet in self.snippets: snippet.delete() # delete from db current_app.mdb.workshop_collections.delete_one({"_id": self.id}) # delete subscriptions self.sub_coll(current_app.mdb).delete_many({"object_id": self.id}) # delete from elasticsearch requests.delete( f"{config.ELASTICSEARCH_ENDPOINT}/workshop_collections/_doc/{str(self.id)}" )
def add_editor(self, user_id: int): """Adds the user to the editor list of this object.""" if self.is_editor(user_id): raise NotAllowed("This user is already an editor.") self.sub_coll(current_app.mdb).insert_one({"type": "editor", "subscriber_id": user_id, "object_id": self.id})