def test_owners_to_string(self):
        owners = [
            {"user": {"username": "******"}},
            {"user": {"username": "******"}}
        ]

        out = model_access.owners_to_string(owners)
        self.assertEqual(out, "['jack', 'jill']")

        owners = models.Profile.objects.filter(user__username__istartswith='j')
        out = model_access.owners_to_string(owners, True)
        self.assertEqual(out, "['jones', 'julia']")
Example #2
0
    def test_owners_to_string(self):
        owners = [{
            "user": {
                "username": "******"
            }
        }, {
            "user": {
                "username": "******"
            }
        }]

        out = model_access.owners_to_string(owners)
        self.assertEqual(out, "['jack', 'jill']")

        owners = models.Profile.objects.filter(user__username__istartswith='j')
        out = model_access.owners_to_string(owners, True)
        self.assertEqual(out, "['johnson', 'jones', 'jsnow', 'julia']")
Example #3
0
    def test_owners_to_string_dict(self):
        owners = [
            {"user": {"username": "******"}},
            {"user": {"username": "******"}}
        ]

        out = model_access.owners_to_string(owners)
        self.assertEqual(out, str(['jack', 'jill']))
Example #4
0
    def update(self, instance, validated_data):
        logger.debug('inside ListingSerializer.update')
        user = generic_model_access.get_profile(
            self.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 is not an owner of this listing')

        change_details = []

        simple_fields = ['title', 'description', 'description_short',
            'launch_url', 'version_name', '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(user, instance)
                change_details.append({'field_name': 'is_enabled',
                    'old_value': 'false', 'new_value': 'true'})
            else:
                model_access.disable_listing(user, instance)
                change_details.append({'field_name': 'is_enabled',
                    'old_value': 'true', 'new_value': 'false'})

            instance.is_enabled = validated_data['is_enabled']

        if validated_data['is_private'] != instance.is_private:
            change_details.append({'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'})
            instance.is_private = validated_data['is_private']

        if 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']

        s = validated_data['approval_status']
        if s and s != 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.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')

        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(%s)'%image_key)
                new_value = model_access.image_to_string(validated_data[image_key], False, 'new_value(%s)'%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:
                change_details.append({'old_value': old_categories,
                    'new_value': new_categories, 'field_name': 'categories'})
                instance.categories.clear()
                for category in validated_data['categories']:
                    instance.categories.add(category)

        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:
                change_details.append({'old_value': old_tags,
                    'new_value': new_tags, 'field_name': 'tags'})
                instance.tags.clear()
                for tag in validated_data['tags']:
                    obj, created = models.Tag.objects.get_or_create(
                        name=tag['name'])
                    instance.tags.add(obj)

        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: %s' % i.id)
                        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(
                    small_image=new_small_image,
                    large_image=new_large_image,
                    listing=instance)


                new_screenshot_instances.append(obj)

            for i in old_screenshot_instances:
                if i not in new_screenshot_instances:
                    logger.info('Deleting screenshot: %s' % i.id)
                    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()

        model_access.log_listing_modification(user, instance, change_details)
        instance.edited_date = datetime.datetime.now(pytz.utc)
        return instance
    def test_update_listing_full(self):
        user = generic_model_access.get_profile('julia').user
        self.client.force_authenticate(user=user)
        url = '/api/listing/1/'
        title = 'julias app 2'
        data = {
            "title": title,
            "description": "description of app",
            "launch_url": "http://www.google.com/launch",
            "version_name": "2.1.8",
            "unique_name": "org.apps.julia-one",
            "what_is_new": "nothing is new",
            "description_short": "a shorter description",
            "requirements": "Many new things",
            "is_private": "true",
            "is_enabled": "false",
            "is_featured": "false",
            "contacts": [
                {"email": "*****@*****.**", "secure_phone": "111-222-3434",
                    "unsecure_phone": "444-555-4545", "name": "me",
                    "contact_type": {"name": "Government"}
                },
                {"email": "*****@*****.**", "secure_phone": "222-222-3333",
                    "unsecure_phone": "555-555-5555", "name": "you",
                    "contact_type": {"name": "Military"}
                }
            ],
            "security_marking": "SECRET",
            "listing_type": {"title": "widget"},
            "small_icon": {"id": 1},
            "large_icon": {"id": 2},
            "banner_icon": {"id": 3},
            "large_banner_icon": {"id": 4},
            "categories": [
                {"title": "Business"},
                {"title": "Education"}
            ],
            "owners": [
                {"user": {"username": "******"}},
                {"user": {"username": "******"}}
            ],
            "tags": [
                {"name": "demo"},
                {"name": "map"}
            ],
            "intents": [
                {"action": "/application/json/view"},
                {"action": "/application/json/edit"}
            ],
            "doc_urls": [
                {"name": "wiki", "url": "http://www.google.com/wiki2"},
                {"name": "guide", "url": "http://www.google.com/guide2"}
            ],
            "screenshots": [
                {"small_image": {"id": 1}, "large_image": {"id": 2}},
                {"small_image": {"id": 3}, "large_image": {"id": 4}}
            ]

        }
        # for checking Activity status later on
        old_listing_data = self.client.get(url, format='json').data
        response = self.client.put(url, data, format='json')

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # title
        self.assertEqual(response.data['title'], data['title'])
        # description
        self.assertEqual(response.data['description'], data['description'])
        # launch_url
        self.assertEqual(response.data['launch_url'], data['launch_url'])
        # version_name
        self.assertEqual(response.data['version_name'], data['version_name'])
        # unique_name
        self.assertEqual(response.data['unique_name'], data['unique_name'])
        # what_is_new
        self.assertEqual(response.data['what_is_new'], data['what_is_new'])
        # description_short
        self.assertEqual(response.data['description_short'], data['description_short'])
        # requirements
        self.assertEqual(response.data['requirements'], data['requirements'])
        # is_private
        self.assertEqual(response.data['is_private'], True)
        # contacts
        self.assertEqual(len(response.data['contacts']), 2)
        names = []
        for c in response.data['contacts']:
            names.append(c['name'])
        self.assertTrue('me' in names)
        self.assertTrue('you' in names)
        # security_marking
        self.assertEqual(response.data['security_marking'],
            'SECRET')
        # listing_type
        self.assertEqual(response.data['listing_type']['title'],
            'widget')
        # icons
        self.assertEqual(response.data['small_icon']['id'], 1)
        self.assertEqual(response.data['large_icon']['id'], 2)
        self.assertEqual(response.data['banner_icon']['id'], 3)
        self.assertEqual(response.data['large_banner_icon']['id'], 4)
        # categories
        categories = []
        for c in response.data['categories']:
            categories.append(c['title'])
        self.assertEqual(len(response.data['categories']), 2)
        self.assertTrue('Business' in categories)
        self.assertTrue('Education' in categories)
        # owners
        owners = []
        for o in response.data['owners']:
            owners.append(o['user']['username'])
        self.assertEqual(len(response.data['owners']), 2)
        self.assertTrue('wsmith' in owners)
        self.assertTrue('julia' in owners)
        # tags
        tags = []
        for t in response.data['tags']:
            tags.append(t['name'])
        self.assertEqual(len(response.data['tags']), 2)
        self.assertTrue('demo' in tags)
        self.assertTrue('map' in tags)
        # intents
        intents = []
        for i in response.data['intents']:
            intents.append(i['action'])
        self.assertEqual(len(response.data['intents']), 2)
        self.assertTrue('/application/json/view' in intents)
        self.assertTrue('/application/json/edit' in intents)
        # doc_urls
        doc_urls = []
        for d in response.data['doc_urls']:
            doc_urls.append(d['url'])
        self.assertEqual(len(response.data['doc_urls']), 2)
        self.assertTrue('http://www.google.com/wiki2' in doc_urls)
        self.assertTrue('http://www.google.com/guide2' in doc_urls)
        # screenshots
        screenshots_small = []
        self.assertEqual(len(response.data['screenshots']), 2)
        for s in response.data['screenshots']:
            screenshots_small.append(s['small_image']['id'])
        self.assertTrue(1 in screenshots_small)
        self.assertTrue(3 in screenshots_small)

        screenshots_large = []
        for s in response.data['screenshots']:
            screenshots_large.append(s['large_image']['id'])
        self.assertTrue(2 in screenshots_large)
        self.assertTrue(4 in screenshots_large)

        self.assertTrue(response.data['approved_date'])
        self.assertEqual(response.data['approval_status'],
            models.Listing.APPROVED)
        self.assertEqual(response.data['is_enabled'], False)
        self.assertEqual(response.data['is_featured'], False)
        self.assertEqual(response.data['avg_rate'], 3.0)
        self.assertEqual(response.data['total_votes'], 3)
        self.assertEqual(response.data['total_rate5'], 1)
        self.assertEqual(response.data['total_rate4'], 0)
        self.assertEqual(response.data['total_rate3'], 1)
        self.assertEqual(response.data['total_rate2'], 0)
        self.assertEqual(response.data['total_rate1'], 1)
        self.assertEqual(response.data['total_reviews'], 3)
        self.assertEqual(response.data['iframe_compatible'], False)
        self.assertEqual(response.data['required_listings'], None)
        self.assertTrue(response.data['edited_date'])
        self.assertEquals(self._validate_listing_map_keys(response.data), [])

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #                   verify change_details
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        activity_url = url + 'activity/'
        activity_response = self.client.get(activity_url, format='json')
        activity_data = activity_response.data

        fields = ['title', 'description', 'description_short', 'version_name',
            'requirements', 'unique_name', 'what_is_new', 'launch_url',
            'is_enabled', 'is_featured', 'is_private', 'doc_urls', 'contacts',
            'screenshots', 'categories', 'owners', 'tags', 'small_icon',
            'large_icon', 'banner_icon', 'large_banner_icon', 'security_marking',
            'listing_type', 'approval_status', 'intents']

        total_found = 0
        for activity in activity_data:
            if activity['action'] == 'MODIFIED':
                for change in activity['change_details']:
                    if change['field_name'] == 'title':
                        self.assertEqual(change['new_value'], data['title'])
                        self.assertEqual(change['old_value'], old_listing_data['title'])
                        total_found += 1
                    if change['field_name'] == 'description':
                        self.assertEqual(change['new_value'], data['description'])
                        self.assertEqual(change['old_value'], old_listing_data['description'])
                        total_found += 1
                    if change['field_name'] == 'description_short':
                        self.assertEqual(change['new_value'], data['description_short'])
                        self.assertEqual(change['old_value'], old_listing_data['description_short'])
                        total_found += 1
                    if change['field_name'] == 'version_name':
                        self.assertEqual(change['new_value'], data['version_name'])
                        self.assertEqual(change['old_value'], old_listing_data['version_name'])
                        total_found += 1
                    if change['field_name'] == 'requirements':
                        self.assertEqual(change['new_value'], data['requirements'])
                        self.assertEqual(change['old_value'], old_listing_data['requirements'])
                        total_found += 1
                    if change['field_name'] == 'what_is_new':
                        self.assertEqual(change['new_value'], data['what_is_new'])
                        self.assertEqual(change['old_value'], old_listing_data['what_is_new'])
                        total_found += 1
                    if change['field_name'] == 'unique_name':
                        self.assertEqual(change['new_value'], data['unique_name'])
                        self.assertEqual(change['old_value'], old_listing_data['unique_name'])
                        total_found += 1
                    if change['field_name'] == 'launch_url':
                        self.assertEqual(change['new_value'], data['launch_url'])
                        self.assertEqual(change['old_value'], old_listing_data['launch_url'])
                        total_found += 1
                    if change['field_name'] == 'is_private':
                        self.assertEqual(change['new_value'], data['is_private'])
                        self.assertEqual(change['old_value'], model_access.bool_to_string(old_listing_data['is_private']))
                        total_found += 1
                    if change['field_name'] == 'is_featured':
                        self.assertEqual(change['new_value'], data['is_featured'])
                        self.assertEqual(change['old_value'], model_access.bool_to_string(old_listing_data['is_featured']))
                        total_found += 1
                    if change['field_name'] == 'listing_type':
                        self.assertEqual(change['new_value'], data['listing_type']['title'])
                        self.assertEqual(change['old_value'], old_listing_data['listing_type']['title'])
                        total_found += 1
                    if change['field_name'] == 'security_marking':
                        self.assertEqual(change['new_value'], data['security_marking'])
                        self.assertEqual(change['old_value'], old_listing_data['security_marking'])
                        total_found += 1
                    if change['field_name'] == 'small_icon':
                        self.assertEqual(change['new_value'], str(data['small_icon']['id']) + '.UNCLASSIFIED')
                        self.assertEqual(change['old_value'], str(old_listing_data['small_icon']['id']) + '.UNCLASSIFIED')
                        total_found += 1
                    if change['field_name'] == 'large_icon':
                        self.assertEqual(change['new_value'], str(data['large_icon']['id']) + '.UNCLASSIFIED')
                        self.assertEqual(change['old_value'], str(old_listing_data['large_icon']['id']) + '.UNCLASSIFIED')
                        total_found += 1
                    if change['field_name'] == 'banner_icon':
                        self.assertEqual(change['new_value'], str(data['banner_icon']['id']) + '.UNCLASSIFIED')
                        self.assertEqual(change['old_value'], str(old_listing_data['banner_icon']['id']) + '.UNCLASSIFIED')
                        total_found += 1
                    if change['field_name'] == 'large_banner_icon':
                        self.assertEqual(change['new_value'], str(data['large_banner_icon']['id']) + '.UNCLASSIFIED')
                        self.assertEqual(change['old_value'], str(old_listing_data['large_banner_icon']['id']) + '.UNCLASSIFIED')
                        total_found += 1
                    if change['field_name'] == 'doc_urls':
                        self.assertEqual(change['new_value'],
                            model_access.doc_urls_to_string(data['doc_urls']))
                        self.assertEqual(change['old_value'],
                            model_access.doc_urls_to_string(old_listing_data['doc_urls']))
                        total_found += 1
                    if change['field_name'] == 'screenshots':
                        self.assertEqual(change['new_value'],
                            model_access.screenshots_to_string(data['screenshots']))
                        self.assertEqual(change['old_value'],
                            model_access.screenshots_to_string(old_listing_data['screenshots']))
                        total_found += 1
                    if change['field_name'] == 'contacts':
                        self.assertEqual(change['new_value'],
                            model_access.contacts_to_string(data['contacts']))
                        self.assertEqual(change['old_value'],
                            model_access.contacts_to_string(old_listing_data['contacts']))
                        total_found += 1
                    if change['field_name'] == 'intents':
                        self.assertEqual(change['new_value'],
                            model_access.intents_to_string(data['intents']))
                        self.assertEqual(change['old_value'],
                            model_access.intents_to_string(old_listing_data['intents']))
                        total_found += 1
                    if change['field_name'] == 'categories':
                        self.assertEqual(change['new_value'],
                            model_access.categories_to_string(data['categories']))
                        self.assertEqual(change['old_value'],
                            model_access.categories_to_string(old_listing_data['categories']))
                        total_found += 1
                    if change['field_name'] == 'tags':
                        self.assertEqual(change['new_value'],
                            model_access.tags_to_string(data['tags']))
                        self.assertEqual(change['old_value'],
                            model_access.tags_to_string(old_listing_data['tags']))
                        total_found += 1
                    if change['field_name'] == 'owners':
                        self.assertEqual(change['new_value'],
                            model_access.owners_to_string(data['owners']))
                        self.assertEqual(change['old_value'],
                            model_access.owners_to_string(old_listing_data['owners']))
                        total_found += 1

        self.assertEqual(total_found, len(fields) - 2)    # (-1 for approved_status) + (-1 for is_enabled)
    def update(self, instance, validated_data):
        user = generic_model_access.get_profile(
            self.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 is not an owner of this listing')

        change_details = []

        simple_fields = ['title', 'description', 'description_short',
            'launch_url', 'version_name', '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(user, instance)
                change_details.append({'field_name': 'is_enabled',
                    'old_value': 'false', 'new_value': 'true'})
            else:
                model_access.disable_listing(user, instance)
                change_details.append({'field_name': 'is_enabled',
                    'old_value': 'true', 'new_value': 'false'})

            instance.is_enabled = validated_data['is_enabled']

        if validated_data['is_private'] != instance.is_private:
            change_details.append({'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'})
            instance.is_private = validated_data['is_private']

        if 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']

        s = validated_data['approval_status']
        if s and s != 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.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')

        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']

        if instance.small_icon != validated_data['small_icon']:
            if instance.small_icon:
                old_value = instance.small_icon.id
            else:
                old_value = None
            if validated_data['small_icon']:
                new_value = validated_data['small_icon'].id
            else:
                new_value = None

            change_details.append({'old_value': old_value,
                    'new_value': new_value, 'field_name': 'small_icon'})
            instance.small_icon = validated_data['small_icon']

        if instance.large_icon != validated_data['large_icon']:
            if instance.large_icon:
                old_value = instance.large_icon.id
            else:
                old_value = None
            if validated_data['large_icon']:
                new_value = validated_data['large_icon'].id
            else:
                new_value = None
            change_details.append({'old_value': old_value,
                    'new_value': new_value, 'field_name': 'large_icon'})
            instance.large_icon = validated_data['large_icon']

        if instance.banner_icon != validated_data['banner_icon']:
            if instance.banner_icon:
                old_value = instance.banner_icon.id
            else:
                old_value = None
            if validated_data['banner_icon']:
                new_value = validated_data['banner_icon'].id
            else:
                new_value = None
            change_details.append({'old_value': old_value,
                    'new_value': new_value, 'field_name': 'banner_icon'})
            instance.banner_icon = validated_data['banner_icon']

        if instance.large_banner_icon != validated_data['large_banner_icon']:
            if instance.large_banner_icon:
                old_value = instance.large_banner_icon.id
            else:
                old_value = None
            if validated_data['large_banner_icon']:
                new_value = validated_data['large_banner_icon'].id
            else:
                new_value = None
            change_details.append({'old_value': old_value,
                    'new_value': new_value, 'field_name': 'large_banner_icon'})
            instance.large_banner_icon = validated_data['large_banner_icon']

        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']:
                    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:
                change_details.append({'old_value': old_categories,
                    'new_value': new_categories, 'field_name': 'categories'})
                instance.categories.clear()
                for category in validated_data['categories']:
                    instance.categories.add(category)

        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:
                change_details.append({'old_value': old_tags,
                    'new_value': new_tags, 'field_name': 'tags'})
                instance.tags.clear()
                for tag in validated_data['tags']:
                    obj, created = models.Tag.objects.get_or_create(
                        name=tag['name'])
                    instance.tags.add(obj)

        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: %s' % i.id)
                        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']:
                obj, created = models.Screenshot.objects.get_or_create(
                    small_image=image_model_access.get_image_by_id(
                        s['small_image']['id']),
                    large_image=image_model_access.get_image_by_id(
                        s['large_image']['id']),
                    listing=instance)
                new_screenshot_instances.append(obj)
            for i in old_screenshot_instances:
                if i not in new_screenshot_instances:
                    logger.info('Deleting screenshot: %s' % i.id)
                    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()

        model_access.log_listing_modification(user, instance, change_details)
        instance.edited_date = datetime.datetime.now(pytz.utc)
        return instance
Example #7
0
 def test_owners_to_string_object(self):
     owners = models.Profile.objects.filter(user__username__istartswith='j')
     out = model_access.owners_to_string(owners, True)
     self.assertEqual(out, "['johnson', 'jones', 'jsnow', 'julia']")
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
Example #9
0
    def update(self, instance, validated_data):
        # logger.debug('inside ListingSerializer.update', extra={'request':self.context.get('request')})
        user = generic_model_access.get_profile(
            self.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', '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(user, instance)
            else:
                model_access.disable_listing(user, instance)

            instance.is_enabled = validated_data['is_enabled']

        if validated_data['is_private'] != instance.is_private:
            change_details.append({
                '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'
            })
            instance.is_private = validated_data['is_private']

        if 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']

        s = validated_data['approval_status']
        if s and s != 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.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')

        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:
                change_details.append({
                    'old_value': old_categories,
                    'new_value': new_categories,
                    'field_name': 'categories'
                })
                instance.categories.clear()
                for category in validated_data['categories']:
                    instance.categories.add(category)

        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:
                change_details.append({
                    'old_value': old_tags,
                    'new_value': new_tags,
                    'field_name': 'tags'
                })
                instance.tags.clear()
                for tag in validated_data['tags']:
                    obj, created = models.Tag.objects.get_or_create(
                        name=tag['name'])
                    instance.tags.add(obj)

        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': self.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(
                    small_image=new_small_image,
                    large_image=new_large_image,
                    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': self.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)

        instance.edited_date = datetime.datetime.now(pytz.utc)
        return instance