def pending_delete_listing(author, listing, pending_description): """ Submit a listing for Deletion Args: author listing Return: listing """ # TODO: check that all required fields are set old_approval_status = listing.approval_status listing = _add_listing_activity(author, listing, models.ListingActivity.PENDING_DELETION, description=pending_description) listing.approval_status = models.Listing.PENDING_DELETION listing.is_enabled = False listing.edited_date = utils.get_now_utc() listing.save() dispatcher.publish('listing_approval_status_changed', listing=listing, profile=author, old_approval_status=old_approval_status, new_approval_status=listing.approval_status) return listing
def reject_listing(steward, listing, rejection_description): """ Reject a submitted listing Args: steward listing rejection_description Return: Listing """ old_approval_status = listing.approval_status listing = _add_listing_activity(steward, listing, models.ListingActivity.REJECTED, description=rejection_description) listing.approval_status = models.Listing.REJECTED listing.edited_date = utils.get_now_utc() listing.save() dispatcher.publish('listing_approval_status_changed', listing=listing, profile=steward, old_approval_status=old_approval_status, new_approval_status=listing.approval_status) return listing
def delete_bookmark_entry_for_profile(request_profile, bookmark_entry_instance): """ Delete Bookmark Entry for profile Validate make sure user has access Deleting a BookmarkEntry that is a folder will have this behavior BookmarkEntry.manager.get(id=5).delete() DELETE FROM "bookmark_parents" WHERE "bookmark_parents"."from_bookmarkentry_id" IN (5) DELETE FROM "bookmark_parents" WHERE "bookmark_parents"."to_bookmarkentry_id" IN (5) DELETE FROM "ozpcenter_bookmarkpermission" WHERE "ozpcenter_bookmarkpermission"."bookmark_id" IN (5) DELETE FROM "ozpcenter_bookmarkentry" WHERE "ozpcenter_bookmarkentry"."id" IN (5) (5, {'ozpcenter.BookmarkEntry': 1, 'ozpcenter.BookmarkEntry_bookmark_parent': 3, 'ozpcenter.BookmarkPermission': 1}) """ check_permission_for_bookmark_entry(request_profile, bookmark_entry_instance) if bookmark_entry_instance.type == FOLDER_TYPE: # root_folder = create_get_user_root_bookmark_folder(request_profile) folder_queries = build_folder_queries_recursive_flatten(request_profile, bookmark_entry_instance, is_start=True) dispatcher.publish('remove_bookmark_folder', bookmark_entry=bookmark_entry_instance, folder_queries=folder_queries) for current_query in folder_queries: current_query.delete() elif bookmark_entry_instance.type == LISTING_TYPE: # If bookmark_entry_instance type is LISTING_TYPE then just delete bookmark_entry_instance.delete()
def _get_profile_by_dn(dn, issuer_dn='default issuer dn'): """ Returns a user profile for a given DN If a profile isn't found with the given DN, create one """ # look up the user with this dn. if the user doesn't exist, create them profile = models.Profile.objects.filter(dn__iexact=dn).first() if profile: if not profile.user.is_active: logger.warning( 'User {0!s} tried to login but is inactive'.format(dn)) return None # update the issuer_dn if profile.issuer_dn != issuer_dn: logger.info('updating issuer dn for user {0!s}'.format( profile.user.username)) profile.issuer_dn = issuer_dn profile.save() return profile else: logger.info('creating new user for dn: {0!s}'.format(dn)) if 'CN=' in dn: cn = utils.find_between(dn, 'CN=', ',') else: cn = dn kwargs = {'display_name': cn, 'dn': dn, 'issuer_dn': issuer_dn} # sanitize username username = cn[0:30] username = username.replace(' ', '_') # no spaces username = username.replace("'", "") # no apostrophes username = username.lower() # all lowercase # make sure this username doesn't exist count = User.objects.filter(username=username).count() if count != 0: new_username = username[0:27] count = User.objects.filter( username__startswith=new_username).count() new_username = '******'.format(new_username, count + 1) username = new_username # now check again - if this username exists, we have a problemce count = User.objects.filter(username=username).count() if count != 0: logger.error( 'Cannot create new user for dn {0!s}, username {1!s} already exists' .format(dn, username)) return None profile = models.Profile.create_user(username, **kwargs) logger.info('created new profile for user {0!s}'.format( profile.user.username)) dispatcher.publish('profile_created', profile=profile) return profile
def update_bookmark_entry_for_profile(request_profile, bookmark_entry_instance, bookmark_parent_object, title): """ Update Bookmark Entries Method Responsibilities: * Rename folder bookmark titles * Moving folder/listing bookmarks under different folders Args: request_profile bookmark_entry_instance bookmark_parent_object: `BookmarkEntry` bookmark instance where request_profile want to put bookmark_entry_instance in that folder title """ if bookmark_parent_object is None and title is None: raise errors.PermissionDenied('Need at least the bookmark_parent or title field') # Check to see if request profile has access to bookmark_entry_instance # (bookmark_entry_instance can be folder or listing bookmark) check_permission_for_bookmark_entry(request_profile, bookmark_entry_instance) folder_title_changed = False bookmark_entry_moved = False if bookmark_parent_object: if bookmark_parent_object.type != FOLDER_TYPE: raise errors.PermissionDenied('bookmark_parent_object needs to be a folder type') # make sure user has owner access on bookmark_parent_object check_permission_for_bookmark_entry(request_profile, bookmark_parent_object) # get bookmark entries to folder relationships for request_profile bookmark_entry_folder_relationships = bookmark_entry_instance.bookmark_parent.filter( bookmark_permission__profile=request_profile) bookmark_entry_instance.bookmark_parent.remove(*bookmark_entry_folder_relationships) bookmark_entry_instance.bookmark_parent.add(bookmark_parent_object) bookmark_entry_moved = True if bookmark_entry_instance.type == FOLDER_TYPE: if title: bookmark_entry_instance.title = title folder_title_changed = True bookmark_entry_instance.save() if bookmark_entry_moved or folder_title_changed: dispatcher.publish('update_bookmark_entry', bookmark_entry_instance=bookmark_entry_instance, bookmark_parent_object=bookmark_parent_object, folder_title_changed=folder_title_changed, bookmark_entry_moved=bookmark_entry_moved) return bookmark_entry_instance
def create_listing_review(username, listing, rating, text=None, review_parent=None, create_day_delta=None): """ Create a new review for a listing Args: username (str): author's username rating (int): rating, 1-5 text (Optional(str)): review text create_date_delta(Optional(int)): Create date in days delta example: 0 = Now, -1 = Minus one day, 1 = plus one day Returns: { "rate": rate, "text": text, "author": author.id, "listing": listing.id, "id": review.id } """ author = generic_model_access.get_profile(username) review = models.Review(listing=listing, author=author, rate=rating, text=text, review_parent=review_parent) if create_day_delta: # created_date = models.DateTimeField(default=utils.get_now_utc) review.created_date = utils.get_now_utc(days_delta=create_day_delta) review.save() # add this action to the log change_details = [ { 'field_name': 'rate', 'old_value': None, 'new_value': rating }, { 'field_name': 'text', 'old_value': None, 'new_value': text } ] listing = review.listing listing = _add_listing_activity(author, listing, models.ListingActivity.REVIEWED, change_details=change_details) # update this listing's rating _update_rating(username, listing) if review.review_parent is None: dispatcher.publish('listing_review_created', listing=listing, profile=author, rating=rating, text=text) return review
def _get_profile_by_dn(dn, issuer_dn='default issuer dn'): """ Returns a user profile for a given DN If a profile isn't found with the given DN, create one """ # look up the user with this dn. if the user doesn't exist, create them profile = models.Profile.objects.filter(dn__iexact=dn).first() if profile: if not profile.user.is_active: logger.warning('User {0!s} tried to login but is inactive'.format(dn)) return None # update the issuer_dn if profile.issuer_dn != issuer_dn: logger.info('updating issuer dn for user {0!s}'.format(profile.user.username)) profile.issuer_dn = issuer_dn profile.save() return profile else: logger.info('creating new user for dn: {0!s}'.format(dn)) if 'CN=' in dn: cn = utils.find_between(dn, 'CN=', ',') else: cn = dn kwargs = {'display_name': cn, 'dn': dn, 'issuer_dn': issuer_dn} # sanitize username username = cn[0:30] username = username.replace(' ', '_') # no spaces username = username.replace("'", "") # no apostrophes username = username.lower() # all lowercase # make sure this username doesn't exist count = User.objects.filter(username=username).count() if count != 0: new_username = username[0:27] count = User.objects.filter(username__startswith=new_username).count() new_username = '******'.format(new_username, count + 1) username = new_username # now check again - if this username exists, we have a problemce count = User.objects.filter(username=username).count() if count != 0: logger.error('Cannot create new user for dn {0!s}, username {1!s} already exists'.format(dn, username)) return None profile = models.Profile.create_user(username, **kwargs) logger.info('created new profile for user {0!s}'.format(profile.user.username)) dispatcher.publish('profile_created', profile=profile) return profile
def create_listing_review(username, listing, rating, text=None, review_parent=None): """ Create a new review for a listing Args: username (str): author's username rating (int): rating, 1-5 text (Optional(str)): review text Returns: { "rate": rate, "text": text, "author": author.id, "listing": listing.id, "id": review.id } """ author = generic_model_access.get_profile(username) review = models.Review(listing=listing, author=author, rate=rating, text=text, review_parent=review_parent) review.save() # update this listing's rating _update_rating(username, listing) #resp = { # "rate": rating, # "text": text, # "author": author.id, # "listing": listing.id, # "id": review.id #} dispatcher.publish('listing_review_created', listing=listing, profile=author, rating=rating, text=text) return review
def edit_listing_review(username, review, rate, text=None): """ Edit an existing review Args: username: user making this request review (models.Review): review to modify rate (int): rating (1-5) text (Optional(str)): review text Returns: The modified review """ # only the author of a review can edit it user = generic_model_access.get_profile(username) if review.author.user.username != username: raise errors.PermissionDenied() change_details = [{ 'field_name': 'rate', 'old_value': review.rate, 'new_value': rate }, { 'field_name': 'text', 'old_value': review.text, 'new_value': text }] listing = review.listing listing = _add_listing_activity(user, listing, models.ListingActivity.REVIEW_EDITED, change_details=change_details) review.rate = rate review.text = text review.edited_date = utils.get_now_utc() review.save() _update_rating(username, listing) dispatcher.publish('listing_review_changed', listing=listing, profile=user, rating=rate, text=text) return review
def delete_listing(username, listing, delete_description): """ TODO: need a way to keep track of this listing as being deleted. for now just remove """ profile = generic_model_access.get_profile(username) # app_owners = [i.user.username for i in listing.owners.all()] # ensure user is the author of this review, or that user is an org # steward or apps mall steward # Don't allow 2nd-party user to be an delete a listing if system_anonymize_identifiable_data(profile.user.username): raise errors.PermissionDenied( 'Current profile does not have delete permissions') priv_roles = ['APPS_MALL_STEWARD', 'ORG_STEWARD'] if profile.highest_role( ) in priv_roles or listing.approval_status == 'IN_PROGRESS': pass else: raise errors.PermissionDenied( 'Only Org Stewards and admins can delete listings') if listing.is_deleted: raise errors.PermissionDenied('The listing has already been deleted') old_approval_status = listing.approval_status listing = _add_listing_activity(profile, listing, models.ListingActivity.DELETED, description=delete_description) listing.is_deleted = True listing.is_enabled = False listing.is_featured = False listing.approval_status = models.Listing.DELETED # TODO Delete the values of other field # Keep lisiting as shell listing for history listing.save() # listing.delete() dispatcher.publish('listing_approval_status_changed', listing=listing, profile=profile, old_approval_status=old_approval_status, new_approval_status=listing.approval_status)
def update_listing(serializer_instance, instance, validated_data): # TODO Put in listing model_access.py user = generic_model_access.get_profile(serializer_instance.context['request'].user.username) if user.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: if user not in instance.owners.all(): raise errors.PermissionDenied( 'User ({0!s}) is not an owner of this listing'.format(user.username)) if instance.is_deleted: raise errors.PermissionDenied('Cannot update a previously deleted listing') change_details = [] simple_fields = ['title', 'description', 'description_short', 'launch_url', 'version_name', 'usage_requirements', 'system_requirements', 'unique_name', 'what_is_new', 'security_marking'] for i in simple_fields: if getattr(instance, i) != validated_data[i]: if validated_data[i] is not None: change_details.append({'old_value': getattr(instance, i), 'new_value': validated_data[i], 'field_name': i}) setattr(instance, i, validated_data[i]) if validated_data['is_enabled'] is not None and validated_data['is_enabled'] != instance.is_enabled: if validated_data['is_enabled']: model_access.enable_listing(user, instance) else: model_access.disable_listing(user, instance) instance.is_enabled = validated_data['is_enabled'] if validated_data['approval_status'] == models.Listing.APPROVED: dispatcher.publish('listing_enabled_status_changed', listing=instance, profile=user, is_enabled=validated_data['is_enabled']) if validated_data['is_508_compliant'] is not None and validated_data['is_508_compliant'] != instance.is_508_compliant: changeset = {'old_value': model_access.bool_to_string(instance.is_508_compliant), 'new_value': model_access.bool_to_string(validated_data['is_508_compliant']), 'field_name': 'is_508_compliant'} change_details.append(changeset) instance.is_508_compliant = validated_data['is_508_compliant'] if validated_data['is_private'] is not None and validated_data['is_private'] != instance.is_private: changeset = {'old_value': model_access.bool_to_string(instance.is_private), 'new_value': model_access.bool_to_string(validated_data['is_private']), 'field_name': 'is_private'} change_details.append(changeset) instance.is_private = validated_data['is_private'] if validated_data['approval_status'] == models.Listing.APPROVED: dispatcher.publish('listing_private_status_changed', listing=instance, profile=user, is_private=validated_data['is_private']) if validated_data['is_featured'] is not None and validated_data['is_featured'] != instance.is_featured: if user.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: raise errors.PermissionDenied('Only stewards can change is_featured setting of a listing') change_details.append({'old_value': model_access.bool_to_string(instance.is_featured), 'new_value': model_access.bool_to_string(validated_data['is_featured']), 'field_name': 'is_featured'}) instance.is_featured = validated_data['is_featured'] instance.featured_date = datetime.datetime.now(pytz.utc) if validated_data['is_featured'] else None s = validated_data['approval_status'] if s and s != instance.approval_status: # Check to see if approval_status has changed old_approval_status = instance.approval_status if s == models.Listing.APPROVED and user.highest_role() != 'APPS_MALL_STEWARD': raise errors.PermissionDenied('Only an APPS_MALL_STEWARD can mark a listing as APPROVED') if s == models.Listing.APPROVED_ORG and user.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: raise errors.PermissionDenied('Only stewards can mark a listing as APPROVED_ORG') if s == models.Listing.PENDING: model_access.submit_listing(user, instance) if s == models.Listing.PENDING_DELETION: # pending deletion should be handled through ListingPendingDeletionViewSet # keeping this here for now for backwards compatibility model_access.pending_delete_listing(user, instance, 'Unknown reason') if s == models.Listing.APPROVED_ORG: model_access.approve_listing_by_org_steward(user, instance) if s == models.Listing.APPROVED: model_access.approve_listing(user, instance) if s == models.Listing.REJECTED: # TODO: need to get the rejection text from somewhere model_access.reject_listing(user, instance, 'TODO: rejection reason') dispatcher.publish('listing_approval_status_changed', listing=instance, profile=user, old_approval_status=old_approval_status, new_approval_status=instance.approval_status) if instance.listing_type != validated_data['listing_type']: if instance.listing_type: old_value = instance.listing_type.title else: old_value = None if validated_data['listing_type']: new_value = validated_data['listing_type'].title else: new_value = None change_details.append({'old_value': old_value, 'new_value': new_value, 'field_name': 'listing_type'}) instance.listing_type = validated_data['listing_type'] image_keys = ['small_icon', 'large_icon', 'banner_icon', 'large_banner_icon'] for image_key in image_keys: if validated_data[image_key]: old_value = model_access.image_to_string(getattr(instance, image_key), True, 'old_value({0!s})'.format(image_key)) new_value = model_access.image_to_string(validated_data[image_key], False, 'new_value({0!s})'.format(image_key)) if old_value != new_value: new_value_image = None old_image_id = None if old_value is not None: old_image_id = getattr(instance, image_key).id if validated_data[image_key].get('id') == old_image_id: new_value_image = getattr(instance, image_key) new_value_image.security_marking = validated_data[image_key].get('security_marking') new_value_image.save() else: new_value_image = image_model_access.get_image_by_id(validated_data[image_key].get('id')) if new_value_image is None: raise errors.InvalidInput('Error while saving, can not find image by id') change_details.append({'old_value': old_value, 'new_value': new_value, 'field_name': image_key}) if image_key == 'small_icon': instance.small_icon = new_value_image elif image_key == 'large_icon': instance.large_icon = new_value_image elif image_key == 'banner_icon': instance.banner_icon = new_value_image elif image_key == 'large_banner_icon': instance.large_banner_icon = new_value_image if 'contacts' in validated_data: old_contact_instances = instance.contacts.all() old_contacts = model_access.contacts_to_string(old_contact_instances, True) new_contacts = model_access.contacts_to_string(validated_data['contacts']) if old_contacts != new_contacts: change_details.append({'old_value': old_contacts, 'new_value': new_contacts, 'field_name': 'contacts'}) instance.contacts.clear() for contact in validated_data['contacts']: # TODO: Smarter Handling of Duplicates Contact Records # A contact with the same name and email should be the same contact # in the backend. # Person1(name='N1',email='*****@*****.**') and # Person1' (name='N1',email='*****@*****.**',secure_phone = '414-444-444') # The two people above should be one contact # if approval_status: "IN_PROGRESS" then it should be using # contact model ids' since it is temporary contacts obj, created = models.Contact.objects.get_or_create( name=contact['name'], email=contact['email'], secure_phone=contact['secure_phone'], unsecure_phone=contact['unsecure_phone'], organization=contact.get('organization', None), contact_type=contact_type_model_access.get_contact_type_by_name( contact['contact_type']['name']) ) instance.contacts.add(obj) if 'categories' in validated_data: old_category_instances = instance.categories.all() old_categories = model_access.categories_to_string(old_category_instances, True) new_categories = model_access.categories_to_string(validated_data['categories'], True) if old_categories != new_categories: changeset = {'old_value': old_categories, 'new_value': new_categories, 'field_name': 'categories'} change_details.append(changeset) instance.categories.clear() for category in validated_data['categories']: instance.categories.add(category) dispatcher.publish('listing_categories_changed', listing=instance, profile=user, old_categories=old_category_instances, new_categories=validated_data['categories']) if 'owners' in validated_data: old_owner_instances = instance.owners.all() old_owners = model_access.owners_to_string(old_owner_instances, True) new_owners = model_access.owners_to_string(validated_data['owners'], True) if old_owners != new_owners: change_details.append({'old_value': old_owners, 'new_value': new_owners, 'field_name': 'owners'}) instance.owners.clear() for owner in validated_data['owners']: instance.owners.add(owner) # tags will be automatically created if necessary if 'tags' in validated_data: old_tag_instances = instance.tags.all() old_tags = model_access.tags_to_string(old_tag_instances, True) new_tags = model_access.tags_to_string(validated_data['tags']) if old_tags != new_tags: changeset = {'old_value': old_tags, 'new_value': new_tags, 'field_name': 'tags'} change_details.append(changeset) instance.tags.clear() new_tags_instances = [] for tag in validated_data['tags']: obj, created = models.Tag.objects.get_or_create( name=tag['name']) instance.tags.add(obj) new_tags_instances.append(obj) dispatcher.publish('listing_tags_changed', listing=instance, profile=user, old_tags=old_tag_instances, new_tags=new_tags_instances) if 'intents' in validated_data: old_intent_instances = instance.intents.all() old_intents = model_access.intents_to_string(old_intent_instances, True) new_intents = model_access.intents_to_string(validated_data['intents'], True) if old_intents != new_intents: change_details.append({'old_value': old_intents, 'new_value': new_intents, 'field_name': 'intents'}) instance.intents.clear() for intent in validated_data['intents']: instance.intents.add(intent) # doc_urls will be automatically created if 'doc_urls' in validated_data: old_doc_url_instances = model_access.get_doc_urls_for_listing(instance) old_doc_urls = model_access.doc_urls_to_string(old_doc_url_instances, True) new_doc_urls = model_access.doc_urls_to_string(validated_data['doc_urls']) if old_doc_urls != new_doc_urls: change_details.append({ 'old_value': old_doc_urls, 'new_value': new_doc_urls, 'field_name': 'doc_urls'}) new_doc_url_instances = [] for d in validated_data['doc_urls']: obj, created = models.DocUrl.objects.get_or_create( name=d['name'], url=d['url'], listing=instance) new_doc_url_instances.append(obj) for i in old_doc_url_instances: if i not in new_doc_url_instances: logger.info('Deleting doc_url: {0!s}'.format(i.id), extra={'request': serializer_instance.context.get('request')}) i.delete() # screenshots will be automatically created if 'screenshots' in validated_data: old_screenshot_instances = model_access.get_screenshots_for_listing(instance) old_screenshots = model_access.screenshots_to_string(old_screenshot_instances, True) new_screenshots = model_access.screenshots_to_string(validated_data['screenshots']) if old_screenshots != new_screenshots: change_details.append({'old_value': old_screenshots, 'new_value': new_screenshots, 'field_name': 'screenshots'}) new_screenshot_instances = [] for s in validated_data['screenshots']: new_small_image = image_model_access.get_image_by_id(s['small_image']['id']) new_small_image.security_marking = s['small_image']['security_marking'] new_small_image.save() new_large_image = image_model_access.get_image_by_id(s['large_image']['id']) new_large_image.security_marking = s['large_image']['security_marking'] new_large_image.save() obj, created = models.Screenshot.objects.get_or_create( order=s.get('order'), small_image=new_small_image, large_image=new_large_image, description=s.get('description'), listing=instance) new_screenshot_instances.append(obj) for i in old_screenshot_instances: if i not in new_screenshot_instances: logger.info('Deleting screenshot: {0!s}'.format(i.id), extra={'request': serializer_instance.context.get('request')}) i.delete() if 'agency' in validated_data: if instance.agency != validated_data['agency']: change_details.append({'old_value': instance.agency.title, 'new_value': validated_data['agency'].title, 'field_name': 'agency'}) instance.agency = validated_data['agency'] instance.save() # If the listing was modified add an entry showing changes if change_details: model_access.log_listing_modification(user, instance, change_details) new_change_details = [] field_to_exclude = ['is_private', 'categories', 'tags'] for change_detail in change_details: if change_detail['field_name'] not in field_to_exclude: new_change_details.append(change_detail) if new_change_details: dispatcher.publish('listing_changed', listing=instance, profile=user, change_details=new_change_details) instance.edited_date = datetime.datetime.now(pytz.utc) return instance
def create_listing(serializer_instance, validated_data): # TODO Put in listing model_access.py title = validated_data['title'] user = generic_model_access.get_profile(serializer_instance.context['request'].user.username) logger.info('creating listing {0!s} for user {1!s}'.format(title, user.user.username), extra={'request': serializer_instance.context.get('request')}) # TODO required_listings listing = models.Listing(title=title, agency=validated_data['agency'], description=validated_data['description'], launch_url=validated_data['launch_url'], version_name=validated_data['version_name'], unique_name=validated_data['unique_name'], what_is_new=validated_data['what_is_new'], description_short=validated_data['description_short'], usage_requirements=validated_data['usage_requirements'], system_requirements=validated_data['system_requirements'], security_marking=validated_data['security_marking'], listing_type=validated_data['listing_type']) # is_enabled = models.BooleanField(default=True) if validated_data['is_enabled'] is not None: listing.is_enabled = validated_data['is_enabled'] # is_featured = models.BooleanField(default=False) if validated_data['is_featured'] is not None: listing.is_featured = validated_data['is_featured'] # is_private = models.BooleanField(default=False) if validated_data['is_private'] is not None: listing.is_private = validated_data['is_private'] # is_508_compliant = models.BooleanField(default=False) if validated_data['is_508_compliant'] is not None: listing.is_508_compliant = validated_data['is_508_compliant'] image_keys = ['small_icon', 'large_icon', 'banner_icon', 'large_banner_icon'] for image_key in image_keys: if validated_data[image_key]: new_value_image = image_model_access.get_image_by_id(validated_data[image_key].get('id')) if new_value_image is None: raise errors.InvalidInput('Error while saving, can not find image by id') if image_key == 'small_icon': listing.small_icon = new_value_image elif image_key == 'large_icon': listing.large_icon = new_value_image elif image_key == 'banner_icon': listing.banner_icon = new_value_image elif image_key == 'large_banner_icon': listing.large_banner_icon = new_value_image listing.save() if validated_data.get('contacts') is not None: for contact in validated_data['contacts']: contact_type_instance = contact_type_model_access.get_contact_type_by_name(contact['contact_type']['name']) new_contact, created = models.Contact.objects.get_or_create(name=contact['name'], email=contact['email'], secure_phone=contact['secure_phone'], unsecure_phone=contact['unsecure_phone'], organization=contact.get('organization', None), contact_type=contact_type_instance) new_contact.save() listing.contacts.add(new_contact) if validated_data.get('owners') is not None: if validated_data['owners']: for owner in validated_data['owners']: listing.owners.add(owner) else: # if no owners are specified, just add the current user listing.owners.add(user) if validated_data.get('categories') is not None: for category in validated_data['categories']: listing.categories.add(category) # tags will be automatically created if necessary if validated_data.get('tags') is not None: for tag in validated_data['tags']: obj, created = models.Tag.objects.get_or_create( name=tag['name']) listing.tags.add(obj) if validated_data.get('intents') is not None: for intent in validated_data['intents']: listing.intents.add(intent) # doc_urls will be automatically created if validated_data.get('doc_urls') is not None: for d in validated_data['doc_urls']: doc_url = models.DocUrl(name=d['name'], url=d['url'], listing=listing) doc_url.save() # screenshots will be automatically created if validated_data.get('screenshots') is not None: for screenshot_dict in validated_data['screenshots']: screenshot = models.Screenshot( order=screenshot_dict.get('order'), small_image=image_model_access.get_image_by_id(screenshot_dict['small_image']['id']), large_image=image_model_access.get_image_by_id(screenshot_dict['large_image']['id']), description=screenshot_dict.get('description'), listing=listing) screenshot.save() # create a new activity model_access.create_listing(user, listing) dispatcher.publish('listing_created', listing=listing, profile=user) return listing
def update_listing(serializer_instance, instance, validated_data): # TODO Put in listing model_access.py user = serializer_instance.context['request'].user profile = generic_model_access.get_profile(user.username) if profile.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: if profile not in instance.owners.all(): raise errors.PermissionDenied( 'User ({0!s}) is not an owner of this listing'.format(user.username)) if instance.is_deleted: raise errors.PermissionDenied('Cannot update a previously deleted listing') change_details = [] simple_fields = ['title', 'description', 'description_short', 'launch_url', 'version_name', 'usage_requirements', 'system_requirements', 'unique_name', 'what_is_new', 'security_marking'] for i in simple_fields: if getattr(instance, i) != validated_data[i]: change_details.append({'old_value': getattr(instance, i), 'new_value': validated_data[i], 'field_name': i}) setattr(instance, i, validated_data[i]) if validated_data['is_enabled'] != instance.is_enabled: if validated_data['is_enabled']: model_access.enable_listing(profile, instance) else: model_access.disable_listing(profile, instance) instance.is_enabled = validated_data['is_enabled'] if validated_data['approval_status'] == models.Listing.APPROVED: dispatcher.publish('listing_enabled_status_changed', listing=instance, profile=profile, is_enabled=validated_data['is_enabled']) if validated_data['is_508_compliant'] != instance.is_508_compliant: changeset = {'old_value': model_access.bool_to_string(instance.is_508_compliant), 'new_value': model_access.bool_to_string(validated_data['is_508_compliant']), 'field_name': 'is_508_compliant'} change_details.append(changeset) instance.is_508_compliant = validated_data['is_508_compliant'] if validated_data['is_private'] != instance.is_private: changeset = {'old_value': model_access.bool_to_string(instance.is_private), 'new_value': model_access.bool_to_string(validated_data['is_private']), 'field_name': 'is_private'} change_details.append(changeset) instance.is_private = validated_data['is_private'] if validated_data['approval_status'] == models.Listing.APPROVED: dispatcher.publish('listing_private_status_changed', listing=instance, profile=profile, is_private=validated_data['is_private']) if validated_data['is_featured'] != instance.is_featured: if profile.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: raise errors.PermissionDenied('Only stewards can change is_featured setting of a listing') change_details.append({'old_value': model_access.bool_to_string(instance.is_featured), 'new_value': model_access.bool_to_string(validated_data['is_featured']), 'field_name': 'is_featured'}) instance.is_featured = validated_data['is_featured'] instance.featured_date = datetime.datetime.now(pytz.utc) if validated_data['is_featured'] else None if validated_data['is_exportable'] != instance.is_exportable: if profile.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: raise errors.PermissionDenied('Only stewards can change is_exportable setting of a listing') change_details.append({'old_value': model_access.bool_to_string(instance.is_exportable), 'new_value': model_access.bool_to_string(validated_data['is_exportable']), 'field_name': 'is_exportable'}) instance.is_exportable = validated_data['is_exportable'] s = validated_data['approval_status'] if s and s != instance.approval_status: # Check to see if approval_status has changed old_approval_status = instance.approval_status if s == models.Listing.APPROVED and profile.highest_role() != 'APPS_MALL_STEWARD': raise errors.PermissionDenied('Only an APPS_MALL_STEWARD can mark a listing as APPROVED') if s == models.Listing.APPROVED_ORG and profile.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: raise errors.PermissionDenied('Only stewards can mark a listing as APPROVED_ORG') if s == models.Listing.PENDING: model_access.submit_listing(profile, instance) if s == models.Listing.PENDING_DELETION: # pending deletion should be handled through ListingPendingDeletionViewSet # keeping this here for now for backwards compatibility model_access.pending_delete_listing(profile, instance, 'Unknown reason') if s == models.Listing.APPROVED_ORG: model_access.approve_listing_by_org_steward(profile, instance) if s == models.Listing.APPROVED: model_access.approve_listing(profile, instance) if s == models.Listing.REJECTED: # TODO: need to get the rejection text from somewhere model_access.reject_listing(profile, instance, 'TODO: rejection reason') dispatcher.publish('listing_approval_status_changed', listing=instance, profile=profile, old_approval_status=old_approval_status, new_approval_status=instance.approval_status) if instance.listing_type != validated_data['listing_type']: if instance.listing_type: old_value = instance.listing_type.title else: old_value = None if validated_data['listing_type']: new_value = validated_data['listing_type'].title else: new_value = None change_details.append({'old_value': old_value, 'new_value': new_value, 'field_name': 'listing_type'}) instance.listing_type = validated_data['listing_type'] image_keys = ['small_icon', 'large_icon', 'banner_icon', 'large_banner_icon'] for image_key in image_keys: if validated_data[image_key]: old_value = model_access.image_to_string(getattr(instance, image_key), True, 'old_value({0!s})'.format(image_key)) new_value = model_access.image_to_string(validated_data[image_key], False, 'new_value({0!s})'.format(image_key)) if old_value != new_value: new_value_image = None old_image_id = None if old_value is not None: old_image_id = getattr(instance, image_key).id if validated_data[image_key].get('id') == old_image_id: new_value_image = getattr(instance, image_key) new_value_image.security_marking = validated_data[image_key].get('security_marking') new_value_image.save() else: new_value_image = image_model_access.get_image_by_id(validated_data[image_key].get('id')) if new_value_image is None: raise errors.InvalidInput('Error while saving, can not find image by id') change_details.append({'old_value': old_value, 'new_value': new_value, 'field_name': image_key}) if image_key == 'small_icon': instance.small_icon = new_value_image elif image_key == 'large_icon': instance.large_icon = new_value_image elif image_key == 'banner_icon': instance.banner_icon = new_value_image elif image_key == 'large_banner_icon': instance.large_banner_icon = new_value_image if 'contacts' in validated_data: old_contact_instances = instance.contacts.all() old_contacts = model_access.contacts_to_string(old_contact_instances, True) new_contacts = model_access.contacts_to_string(validated_data['contacts']) if old_contacts != new_contacts: change_details.append({'old_value': old_contacts, 'new_value': new_contacts, 'field_name': 'contacts'}) instance.contacts.clear() for contact in validated_data['contacts']: # TODO: Smarter Handling of Duplicates Contact Records # A contact with the same name and email should be the same contact # in the backend. # Person1(name='N1',email='*****@*****.**') and # Person1' (name='N1',email='*****@*****.**',secure_phone = '414-444-444') # The two people above should be one contact # if approval_status: "IN_PROGRESS" then it should be using # contact model ids' since it is temporary contacts obj, created = models.Contact.objects.get_or_create( name=contact['name'], email=contact['email'], secure_phone=contact['secure_phone'], unsecure_phone=contact['unsecure_phone'], organization=contact.get('organization', None), contact_type=contact_type_model_access.get_contact_type_by_name( contact['contact_type']['name']) ) instance.contacts.add(obj) if 'categories' in validated_data: old_category_instances = instance.categories.all() old_categories = model_access.categories_to_string(old_category_instances, True) new_categories = model_access.categories_to_string(validated_data['categories'], True) if old_categories != new_categories: changeset = {'old_value': old_categories, 'new_value': new_categories, 'field_name': 'categories'} change_details.append(changeset) instance.categories.clear() for category in validated_data['categories']: instance.categories.add(category) dispatcher.publish('listing_categories_changed', listing=instance, profile=profile, old_categories=old_category_instances, new_categories=validated_data['categories']) if 'owners' in validated_data: old_owner_instances = instance.owners.all() old_owners = model_access.owners_to_string(old_owner_instances, True) new_owners = model_access.owners_to_string(validated_data['owners'], True) if old_owners != new_owners: change_details.append({'old_value': old_owners, 'new_value': new_owners, 'field_name': 'owners'}) instance.owners.clear() for owner in validated_data['owners']: instance.owners.add(owner) # tags will be automatically created if necessary if 'tags' in validated_data: old_tag_instances = instance.tags.all() old_tags = model_access.tags_to_string(old_tag_instances, True) new_tags = model_access.tags_to_string(validated_data['tags']) if old_tags != new_tags: changeset = {'old_value': old_tags, 'new_value': new_tags, 'field_name': 'tags'} change_details.append(changeset) instance.tags.clear() new_tags_instances = [] for tag in validated_data['tags']: obj, created = models.Tag.objects.get_or_create( name=tag['name']) instance.tags.add(obj) new_tags_instances.append(obj) dispatcher.publish('listing_tags_changed', listing=instance, profile=profile, old_tags=old_tag_instances, new_tags=new_tags_instances) # tags will be automatically created if necessary if 'custom_fields' in validated_data: old_custom_field_instances = instance.custom_fields.all() custom_fields = model_access.custom_field_values_to_string(old_custom_field_instances, True) new_custom_fields = model_access.custom_field_values_to_string(validated_data['custom_fields']) if custom_fields != new_custom_fields: changeset = {'old_value': custom_fields, 'new_value': new_custom_fields, 'field_name': 'custom_fields'} change_details.append(changeset) new_custom_fields_instances = [] for cfv in validated_data['custom_fields']: id = cfv['custom_field'].id cf = models.CustomField.objects.find_by_id(id=id) obj, created = models.CustomFieldValue.objects.get_or_create( listing=instance, custom_field=cf, value=cfv['value'] ) if created: instance.custom_fields.add(obj) new_custom_fields_instances.append(obj) dispatcher.publish('listing_custom_field_values_changed', listing=instance, profile=profile, old_tags=old_custom_field_instances, new_tags=new_custom_fields_instances) if 'intents' in validated_data: old_intent_instances = instance.intents.all() old_intents = model_access.intents_to_string(old_intent_instances, True) new_intents = model_access.intents_to_string(validated_data['intents'], True) if old_intents != new_intents: change_details.append({'old_value': old_intents, 'new_value': new_intents, 'field_name': 'intents'}) instance.intents.clear() for intent in validated_data['intents']: instance.intents.add(intent) # doc_urls will be automatically created if 'doc_urls' in validated_data: old_doc_url_instances = model_access.get_doc_urls_for_listing(instance) old_doc_urls = model_access.doc_urls_to_string(old_doc_url_instances, True) new_doc_urls = model_access.doc_urls_to_string(validated_data['doc_urls']) if old_doc_urls != new_doc_urls: change_details.append({ 'old_value': old_doc_urls, 'new_value': new_doc_urls, 'field_name': 'doc_urls'}) new_doc_url_instances = [] for d in validated_data['doc_urls']: obj, created = models.DocUrl.objects.get_or_create( name=d['name'], url=d['url'], listing=instance) new_doc_url_instances.append(obj) for i in old_doc_url_instances: if i not in new_doc_url_instances: logger.info('Deleting doc_url: {0!s}'.format(i.id), extra={'request': serializer_instance.context.get('request')}) i.delete() # screenshots will be automatically created if 'screenshots' in validated_data: old_screenshot_instances = model_access.get_screenshots_for_listing(instance) old_screenshots = model_access.screenshots_to_string(old_screenshot_instances, True) new_screenshots = model_access.screenshots_to_string(validated_data['screenshots']) if old_screenshots != new_screenshots: change_details.append({'old_value': old_screenshots, 'new_value': new_screenshots, 'field_name': 'screenshots'}) new_screenshot_instances = [] for s in validated_data['screenshots']: new_small_image = image_model_access.get_image_by_id(s['small_image']['id']) new_small_image.security_marking = s['small_image']['security_marking'] new_small_image.save() new_large_image = image_model_access.get_image_by_id(s['large_image']['id']) new_large_image.security_marking = s['large_image']['security_marking'] new_large_image.save() obj, created = models.Screenshot.objects.get_or_create( order=s.get('order'), small_image=new_small_image, large_image=new_large_image, description=s.get('description'), listing=instance) new_screenshot_instances.append(obj) for i in old_screenshot_instances: if i not in new_screenshot_instances: logger.info('Deleting screenshot: {0!s}'.format(i.id), extra={'request': serializer_instance.context.get('request')}) i.delete() if 'agency' in validated_data: if instance.agency != validated_data['agency']: change_details.append({'old_value': instance.agency.title, 'new_value': validated_data['agency'].title, 'field_name': 'agency'}) instance.agency = validated_data['agency'] instance.save() # If the listing was modified add an entry showing changes if change_details: model_access.log_listing_modification(profile, instance, change_details) new_change_details = [] field_to_exclude = ['is_private', 'categories', 'tags'] for change_detail in change_details: if change_detail['field_name'] not in field_to_exclude: new_change_details.append(change_detail) if new_change_details: dispatcher.publish('listing_changed', listing=instance, profile=profile, change_details=new_change_details) instance.edited_date = datetime.datetime.now(pytz.utc) return instance
def create_listing(serializer_instance, validated_data): # TODO Put in listing model_access.py user = serializer_instance.context['request'].user profile = generic_model_access.get_profile(user.username) title = validated_data['title'] logger.info('creating listing {0!s} for user {1!s}'.format(title, user.username), extra={'request': serializer_instance.context.get('request')}) # TODO required_listings listing = models.Listing(title=title, agency=validated_data['agency'], description=validated_data['description'], launch_url=validated_data['launch_url'], version_name=validated_data['version_name'], unique_name=validated_data['unique_name'], what_is_new=validated_data['what_is_new'], description_short=validated_data['description_short'], usage_requirements=validated_data['usage_requirements'], system_requirements=validated_data['system_requirements'], security_marking=validated_data['security_marking'], listing_type=validated_data['listing_type'], is_private=validated_data['is_private'], is_508_compliant=validated_data['is_508_compliant']) image_keys = ['small_icon', 'large_icon', 'banner_icon', 'large_banner_icon'] for image_key in image_keys: if validated_data[image_key]: new_value_image = image_model_access.get_image_by_id(validated_data[image_key].get('id')) if new_value_image is None: raise errors.InvalidInput('Error while saving, can not find image by id') if image_key == 'small_icon': listing.small_icon = new_value_image elif image_key == 'large_icon': listing.large_icon = new_value_image elif image_key == 'banner_icon': listing.banner_icon = new_value_image elif image_key == 'large_banner_icon': listing.large_banner_icon = new_value_image listing.save() if validated_data.get('contacts') is not None: for contact in validated_data['contacts']: contact_type_instance = contact_type_model_access.get_contact_type_by_name(contact['contact_type']['name']) new_contact, created = models.Contact.objects.get_or_create(name=contact['name'], email=contact['email'], secure_phone=contact['secure_phone'], unsecure_phone=contact['unsecure_phone'], organization=contact.get('organization', None), contact_type=contact_type_instance) new_contact.save() listing.contacts.add(new_contact) if validated_data.get('owners') is not None: if validated_data['owners']: for owner in validated_data['owners']: listing.owners.add(owner) else: # if no owners are specified, just add the current user listing.owners.add(profile) if validated_data.get('categories') is not None: for category in validated_data['categories']: listing.categories.add(category) # tags will be automatically created if necessary if validated_data.get('tags') is not None: for tag in validated_data['tags']: obj, created = models.Tag.objects.get_or_create( name=tag['name']) listing.tags.add(obj) if validated_data.get('intents') is not None: for intent in validated_data['intents']: listing.intents.add(intent) # doc_urls will be automatically created if validated_data.get('doc_urls') is not None: for d in validated_data['doc_urls']: doc_url = models.DocUrl(name=d['name'], url=d['url'], listing=listing) doc_url.save() # screenshots will be automatically created if validated_data.get('screenshots') is not None: for screenshot_dict in validated_data['screenshots']: screenshot = models.Screenshot( order=screenshot_dict.get('order'), small_image=image_model_access.get_image_by_id(screenshot_dict['small_image']['id']), large_image=image_model_access.get_image_by_id(screenshot_dict['large_image']['id']), description=screenshot_dict.get('description'), listing=listing) screenshot.save() if 'custom_fields' in validated_data: for field_value_dict in validated_data['custom_fields']: field_id = field_value_dict['custom_field'].id field = models.CustomField.objects.find_by_id(id=field_id) value = field_value_dict['value'] field_value = models.CustomFieldValue(listing=listing, custom_field=field, value=value) field_value.save() listing.custom_fields.add(field_value) # create a new activity model_access.create_listing(profile, listing) dispatcher.publish('listing_created', listing=listing, profile=profile) return listing