Exemplo n.º 1
0
 def get_instance_permissions(self, resource_instance):
     permission_order = ["view_resourceinstance", "change_resourceinstance", "delete_resourceinstance", "no_access_to_resourceinstance"]
     perms = json.loads(
         JSONSerializer().serialize(
             {p.codename: p for p in get_perms_for_model(resource_instance) if p.codename != "add_resourceinstance"}
         )
     )
     ordered_perms = []
     for p in permission_order:
         ordered_perms.append(perms[p])
     identities = [
         {
             "name": user.username,
             "id": user.id,
             "type": "user",
             "default_permissions": self.get_perms(user, "user", resource_instance, ordered_perms),
             "is_editor_or_reviewer": bool(user_is_resource_editor(user) or user_is_resource_reviewer(user)),
         }
         for user in User.objects.all()
     ]
     identities += [
         {
             "name": group.name,
             "id": group.id,
             "type": "group",
             "default_permissions": self.get_perms(group, "group", resource_instance, ordered_perms),
         }
         for group in Group.objects.all()
     ]
     result = {"identities": identities}
     result["permissions"] = ordered_perms
     result["limitedaccess"] = (len(get_users_with_perms(resource_instance)) + len(get_groups_with_perms(resource_instance))) > 1
     instance_creator = get_instance_creator(resource_instance)
     result["creatorid"] = instance_creator["creatorid"]
     return result
Exemplo n.º 2
0
 def handle_reviewer_edits(self, user, tile):
     if hasattr(user, "userprofile") is not True:
         models.UserProfile.objects.create(user=user)
     if user_is_resource_reviewer(user):
         user_id = str(user.id)
         if user_id in tile.provisionaledits:
             tile.provisionaledits.pop(user_id, None)
Exemplo n.º 3
0
def get_provisional_type(request):
    """
    Parses the provisional filter data to determine if a search results will
    include provisional (True) exclude provisional (False) or inlude only
    provisional 'only provisional'
    """

    result = False
    provisional_filter = JSONDeserializer().deserialize(
        request.GET.get("provisional-filter", "[]"))
    user_is_reviewer = user_is_resource_reviewer(request.user)
    if user_is_reviewer is not False:
        if len(provisional_filter) == 0:
            result = True
        else:
            inverted = provisional_filter[0]["inverted"]
            if provisional_filter[0]["provisionaltype"] == "Provisional":
                if inverted is False:
                    result = "only provisional"
                else:
                    result = False
            if provisional_filter[0]["provisionaltype"] == "Authoritative":
                if inverted is False:
                    result = False
                else:
                    result = "only provisional"

    return result
Exemplo n.º 4
0
    def delete(self, *args, **kwargs):
        se = SearchEngineFactory().create()
        request = kwargs.pop("request", None)
        provisional_edit_log_details = kwargs.pop("provisional_edit_log_details", None)
        for tile in self.tiles:
            tile.delete(*args, request=request, **kwargs)
        try:
            user = request.user
            user_is_reviewer = user_is_resource_reviewer(user)
        except AttributeError:  # no user
            user = None
            user_is_reviewer = True

        if user_is_reviewer is True or self.user_owns_provisional(user):
            query = Query(se)
            bool_query = Bool()
            bool_query.filter(Terms(field="tileid", terms=[self.tileid]))
            query.add_query(bool_query)
            results = query.search(index="terms")["hits"]["hits"]

            for result in results:
                se.delete(index="terms", id=result["_id"])

            self.__preDelete(request)
            self.save_edit(
                user=request.user, edit_type="tile delete", old_value=self.data, provisional_edit_log_details=provisional_edit_log_details
            )
            super(Tile, self).delete(*args, **kwargs)
            resource = Resource.objects.get(resourceinstanceid=self.resourceinstance.resourceinstanceid)
            resource.index()

        else:
            self.apply_provisional_edit(user, data={}, action="delete")
            super(Tile, self).save(*args, **kwargs)
Exemplo n.º 5
0
    def get(self, request):
        map_layers = models.MapLayer.objects.all()
        map_markers = models.MapMarker.objects.all()
        map_sources = models.MapSource.objects.all()
        resource_graphs = (models.GraphModel.objects.exclude(
            pk=settings.SYSTEM_SETTINGS_RESOURCE_MODEL_ID).exclude(
                isresource=False).exclude(isactive=False))
        geocoding_providers = models.Geocoder.objects.all()
        search_components = models.SearchComponent.objects.all()
        datatypes = models.DDataType.objects.all()
        widgets = models.Widget.objects.all()
        templates = models.ReportTemplate.objects.all()
        card_components = models.CardComponent.objects.all()

        context = self.get_context_data(
            map_layers=map_layers,
            map_markers=map_markers,
            map_sources=map_sources,
            geocoding_providers=geocoding_providers,
            search_components=search_components,
            widgets=widgets,
            report_templates=templates,
            card_components=card_components,
            main_script="views/search",
            resource_graphs=resource_graphs,
            datatypes=datatypes,
            user_is_reviewer=user_is_resource_reviewer(request.user),
        )

        graphs = JSONSerializer().serialize(
            context["resource_graphs"],
            exclude=[
                "functions",
                "author",
                "deploymentdate",
                "deploymentfile",
                "version",
                "subtitle",
                "description",
                "disable_instance_creation",
                "ontology_id",
            ],
        )
        context["graphs"] = graphs
        context["nav"]["title"] = _("Search")
        context["nav"]["icon"] = "fa-search"
        context["nav"]["search"] = False
        context["nav"]["help"] = {
            "title": _("Searching the Database"),
            "template": "search-help",
        }
        context["celery_running"] = task_management.check_if_celery_available()
        context[
            "export_html_templates"] = HtmlWriter.get_graphids_with_export_template(
            )

        return render(request, "views/search.htm", context)
Exemplo n.º 6
0
    def delete(self, user={}, note=""):
        """
        Deletes a single resource and any related indexed data

        """

        permit_deletion = False
        graph = models.GraphModel.objects.get(graphid=self.graph_id)
        if graph.isactive is False:
            message = _("This model is not yet active; unable to delete.")
            raise ModelInactiveError(message)
        if user != {}:
            user_is_reviewer = user_is_resource_reviewer(user)
            if user_is_reviewer is False:
                tiles = list(models.TileModel.objects.filter(resourceinstance=self))
                resource_is_provisional = True if sum([len(t.data) for t in tiles]) == 0 else False
                if resource_is_provisional is True:
                    permit_deletion = True
            else:
                permit_deletion = True
        else:
            permit_deletion = True

        if permit_deletion is True:
            related_resources = self.get_related_resources(lang="en-US", start=0, limit=1000, page=0)
            for rr in related_resources["resource_relationships"]:
                # delete any related resource entries, also reindex the resource that references this resource that's being deleted
                try:
                    resourceXresource = models.ResourceXResource.objects.get(pk=rr["resourcexid"])
                    resource_to_reindex = (
                        resourceXresource.resourceinstanceidfrom_id
                        if resourceXresource.resourceinstanceidto_id == self.resourceinstanceid
                        else resourceXresource.resourceinstanceidto_id
                    )
                    resourceXresource.delete(deletedResourceId=self.resourceinstanceid)
                    res = Resource.objects.get(pk=resource_to_reindex)
                    res.load_tiles()
                    res.index()
                except ObjectDoesNotExist:
                    se.delete(index=RESOURCE_RELATIONS_INDEX, id=rr["resourcexid"])

            query = Query(se)
            bool_query = Bool()
            bool_query.filter(Terms(field="resourceinstanceid", terms=[self.resourceinstanceid]))
            query.add_query(bool_query)
            results = query.search(index=TERMS_INDEX)["hits"]["hits"]
            for result in results:
                se.delete(index=TERMS_INDEX, id=result["_id"])
            se.delete(index=RESOURCES_INDEX, id=self.resourceinstanceid)

            try:
                self.save_edit(edit_type="delete", user=user, note=self.displayname)
            except:
                pass
            super(Resource, self).delete()

        return permit_deletion
Exemplo n.º 7
0
    def get_context_data(self, **kwargs):
        context = super(BaseManagerView, self).get_context_data(**kwargs)
        context[
            "system_settings_graphid"] = settings.SYSTEM_SETTINGS_RESOURCE_MODEL_ID
        context["graph_models"] = []
        context["graphs"] = "[]"
        context["plugins"] = []
        for plugin in models.Plugin.objects.all().order_by("sortorder"):
            if self.request.user.has_perm("view_plugin", plugin):
                context["plugins"].append(plugin)

        createable = get_createable_resource_types(self.request.user)
        createable.sort(key=lambda x: x.name.lower())
        context["createable_resources"] = JSONSerializer().serialize(
            createable,
            exclude=[
                "functions",
                "ontology",
                "isactive",
                "isresource",
                "version",
                "deploymentdate",
                "deploymentfile",
                "author",
            ],
        )

        context["notifications"] = models.UserXNotification.objects.filter(
            recipient=self.request.user, isread=False)
        context["nav"] = {
            "icon": "fa fa-chevron-circle-right",
            "title": "",
            "help": {
                # title:'',template:'' (leave this commented out)
            },
            "menu": False,
            "search": True,
            "notifs": True,
            "res_edit": False,
            "login": True,
            "print": False,
        }
        context["user_is_reviewer"] = user_is_resource_reviewer(
            self.request.user)
        context["user_can_edit"] = len(
            get_editable_resource_types(self.request.user)) > 0
        context["user_can_read"] = (len(
            get_resource_types_by_perm(self.request.user, [
                "models.write_nodegroup", "models.delete_nodegroup",
                "models.read_nodegroup"
            ])) > 0)
        context["app_name"] = settings.APP_NAME
        context["show_language_swtich"] = settings.SHOW_LANGUAGE_SWITCH
        return context
Exemplo n.º 8
0
    def get_tile_data(self, user_id=None):
        data = self.data

        if user_id is not None:
            user_id = str(user_id)
            user = User.objects.get(pk=user_id)
            user_is_reviewer = user_is_resource_reviewer(user)
            if user_is_reviewer is False and self.provisionaledits is not None and user_id in self.provisionaledits:
                data = self.provisionaledits[user_id]["value"]

        return data
Exemplo n.º 9
0
    def post(self, request):

        if self.action == "get_user_names":
            data = {}
            if self.request.user.is_authenticated and user_is_resource_reviewer(request.user):
                userids = json.loads(request.POST.get("userids", "[]"))
                data = {u.id: u.username for u in User.objects.filter(id__in=userids)}
                return JSONResponse(data)

        if self.request.user.is_authenticated and self.request.user.username != "anonymous":

            user_details = self.get_user_details(request.user)

            context = self.get_context_data(
                main_script="views/user-profile-manager",
            )
            context["errors"] = []
            context["nav"]["icon"] = "fa fa-user"
            context["nav"]["title"] = _("Profile Manager")
            context["nav"]["login"] = True
            context["nav"]["help"] = {
                "title": _("Profile Editing"),
                "template": "profile-manager-help",
            }
            context["validation_help"] = validation.password_validators_help_texts()
            context["user_surveys"] = JSONSerializer().serialize(user_details["user_surveys"])
            context["identities"] = JSONSerializer().serialize(user_details["identities"])
            context["resources"] = JSONSerializer().serialize(user_details["resources"])

            user_info = request.POST.copy()
            user_info["id"] = request.user.id
            user_info["username"] = request.user.username
            user_info["password1"] = request.user.password
            user_info["password2"] = request.user.password

            form = ArchesUserProfileForm(user_info)

            if form.is_valid():
                user = form.save()
                try:
                    admin_info = settings.ADMINS[0][1] if settings.ADMINS else None
                    message = (
                        f"Your {settings.APP_NAME} profile was just changed.  If this was unexpected, please contact your "
                        f"{settings.APP_NAME} administrator{f' at {admin_info}.' if (admin_info and not str.isspace(admin_info)) else '.'}"
                    )
                    message = _(message)
                    user.email_user(_("Your " + settings.APP_NAME + " Profile Has Changed"), message)
                except:
                    logger.error("Error sending email", exc_info=True)
                request.user = user
            context["form"] = form

            return render(request, "views/user-profile-manager.htm", context)
Exemplo n.º 10
0
    def delete(self, user={}, note=""):
        """
        Deletes a single resource and any related indexed data

        """

        permit_deletion = False
        graph = models.GraphModel.objects.get(graphid=self.graph_id)
        if graph.isactive is False:
            message = _("This model is not yet active; unable to delete.")
            raise ModelInactiveError(message)
        if user != {}:
            user_is_reviewer = user_is_resource_reviewer(user)
            if user_is_reviewer is False:
                tiles = list(
                    models.TileModel.objects.filter(resourceinstance=self))
                resource_is_provisional = True if sum(
                    [len(t.data) for t in tiles]) == 0 else False
                if resource_is_provisional is True:
                    permit_deletion = True
            else:
                permit_deletion = True
        else:
            permit_deletion = True

        if permit_deletion is True:
            se = SearchEngineFactory().create()
            related_resources = self.get_related_resources(lang="en-US",
                                                           start=0,
                                                           limit=1000,
                                                           page=0)
            for rr in related_resources["resource_relationships"]:
                models.ResourceXResource.objects.get(
                    pk=rr["resourcexid"]).delete()
            query = Query(se)
            bool_query = Bool()
            bool_query.filter(
                Terms(field="resourceinstanceid",
                      terms=[self.resourceinstanceid]))
            query.add_query(bool_query)
            results = query.search(index="terms")["hits"]["hits"]
            for result in results:
                se.delete(index="terms", id=result["_id"])
            se.delete(index="resources", id=self.resourceinstanceid)

            self.save_edit(edit_type="delete",
                           user=user,
                           note=self.displayname)
            super(Resource, self).delete()

        return permit_deletion
Exemplo n.º 11
0
    def delete(self, user={}, index=True, transaction_id=None):
        """
        Deletes a single resource and any related indexed data

        """

        # note that deferring index will require:
        # - that any resources related to the to-be-deleted resource get re-indexed
        # - that the index for the to-be-deleted resource gets deleted

        permit_deletion = False
        graph = models.GraphModel.objects.get(graphid=self.graph_id)
        if graph.isactive is False:
            message = _("This model is not yet active; unable to delete.")
            raise ModelInactiveError(message)
        if user != {}:
            user_is_reviewer = user_is_resource_reviewer(user)
            if user_is_reviewer is False:
                tiles = list(
                    models.TileModel.objects.filter(resourceinstance=self))
                resource_is_provisional = True if sum(
                    [len(t.data) for t in tiles]) == 0 else False
                if resource_is_provisional is True:
                    permit_deletion = True
            else:
                permit_deletion = True
        else:
            permit_deletion = True

        if permit_deletion is True:
            for related_resource in models.ResourceXResource.objects.filter(
                    Q(resourceinstanceidfrom=self.resourceinstanceid)
                    | Q(resourceinstanceidto=self.resourceinstanceid)):
                related_resource.delete(
                    deletedResourceId=self.resourceinstanceid, index=False)

            if index:
                self.delete_index()

            try:
                self.save_edit(edit_type="delete",
                               user=user,
                               note=self.displayname,
                               transaction_id=transaction_id)
            except:
                pass
            super(Resource, self).delete()

        return permit_deletion
Exemplo n.º 12
0
 def datatype_post_save_actions(self, request=None):
     userid = None
     user_is_reviewer = True
     if request is not None:
         userid = str(request.user.id)
         if hasattr(request.user, "userprofile") is not True:
             models.UserProfile.objects.create(user=request.user)
         user_is_reviewer = user_is_resource_reviewer(request.user)
     tile_data = self.get_tile_data(user_is_reviewer, userid)
     for nodeid, value in list(tile_data.items()):
         datatype_factory = DataTypeFactory()
         node = models.Node.objects.get(nodeid=nodeid)
         datatype = datatype_factory.get_instance(node.datatype)
         if request is not None:
             datatype.handle_request(self, request, node)
Exemplo n.º 13
0
    def delete(self, request):
        json = request.body
        if json is not None:
            ret = []
            data = JSONDeserializer().deserialize(json)
            resource_instance = models.ResourceInstance.objects.get(pk=data["resourceinstance_id"])
            is_active = resource_instance.graph.isactive

            with transaction.atomic():
                try:
                    tile = Tile.objects.get(tileid=data["tileid"])
                except ObjectDoesNotExist:
                    return JSONErrorResponse(_("This tile is no longer available"), _("It was likely already deleted by another user"))
                user_is_reviewer = user_is_resource_reviewer(request.user)
                if (user_is_reviewer or tile.is_provisional() is True) and is_active is True:
                    if tile.filter_by_perm(request.user, "delete_nodegroup"):
                        nodegroup = models.NodeGroup.objects.get(pk=tile.nodegroup_id)
                        if tile.is_provisional() is True and len(list(tile.provisionaledits.keys())) == 1:
                            provisional_editor_id = list(tile.provisionaledits.keys())[0]
                            edit = tile.provisionaledits[provisional_editor_id]
                            provisional_editor = User.objects.get(pk=provisional_editor_id)
                            reviewer = request.user
                            tile.delete(
                                request=request,
                                provisional_edit_log_details={
                                    "user": reviewer,
                                    "action": "delete edit",
                                    "edit": edit,
                                    "provisional_editor": provisional_editor,
                                },
                            )
                        else:
                            tile.delete(request=request)
                        tile.after_update_all()
                        update_system_settings_cache(tile)
                        return JSONResponse(tile)
                    else:
                        return JSONErrorResponse(_("Request Failed"), _("Permission Denied"))
                elif is_active is False:
                    response = {"status": "false", "message": [_("Request Failed"), _("Unable to delete. Verify model status is active")]}
                    return JSONResponse(response, status=500)
                else:
                    return JSONErrorResponse(
                        _("Request Failed"), _("You do not have permissions to delete a tile with authoritative data.")
                    )

        return HttpResponseNotFound()
Exemplo n.º 14
0
    def delete(self, *args, **kwargs):
        se = SearchEngineFactory().create()
        request = kwargs.pop("request", None)
        index = kwargs.pop("index", True)
        transaction_id = kwargs.pop("index", None)
        provisional_edit_log_details = kwargs.pop(
            "provisional_edit_log_details", None)
        for tile in self.tiles:
            tile.delete(*args, request=request, **kwargs)
        try:
            user = request.user
            user_is_reviewer = user_is_resource_reviewer(user)
        except AttributeError:  # no user
            user = None
            user_is_reviewer = True

        if user_is_reviewer is True or self.user_owns_provisional(user):
            if index:
                query = Query(se)
                bool_query = Bool()
                bool_query.filter(Terms(field="tileid", terms=[self.tileid]))
                query.add_query(bool_query)
                results = query.delete(index=TERMS_INDEX)

            self.__preDelete(request)
            self.save_edit(
                user=user,
                edit_type="tile delete",
                old_value=self.data,
                provisional_edit_log_details=provisional_edit_log_details,
                transaction_id=transaction_id,
            )
            try:
                super(Tile, self).delete(*args, **kwargs)
                for nodeid in self.data.keys():
                    node = models.Node.objects.get(nodeid=nodeid)
                    datatype = self.datatype_factory.get_instance(
                        node.datatype)
                    datatype.post_tile_delete(self, nodeid, index=index)
                if index:
                    self.index()
            except IntegrityError as e:
                logger.error(e)

        else:
            self.apply_provisional_edit(user, data={}, action="delete")
            super(Tile, self).save(*args, **kwargs)
Exemplo n.º 15
0
    def post_search_hook(self, search_results_object, results, permitted_nodegroups):
        user_is_reviewer = user_is_resource_reviewer(self.request.user)

        # only reuturn points and geometries a user is allowed to view
        geojson_nodes = get_nodegroups_by_datatype_and_perm(self.request, "geojson-feature-collection", "read_nodegroup")

        for result in results["hits"]["hits"]:
            result["_source"]["points"] = select_geoms_for_results(result["_source"]["points"], geojson_nodes, user_is_reviewer)
            result["_source"]["geometries"] = select_geoms_for_results(result["_source"]["geometries"], geojson_nodes, user_is_reviewer)
            try:
                permitted_tiles = []
                for tile in result["_source"]["tiles"]:
                    if tile["nodegroup_id"] in permitted_nodegroups:
                        permitted_tiles.append(tile)
                result["_source"]["tiles"] = permitted_tiles
            except KeyError:
                pass
Exemplo n.º 16
0
    def post(self, request):
        username = request.POST.get("username", None)
        password = request.POST.get("password", None)
        user = authenticate(username=username, password=password)
        if user:
            if hasattr(user, "userprofile") is not True:
                models.UserProfile.objects.create(user=user)
            userDict = JSONSerializer().serializeToPython(user)
            userDict["password"] = None
            userDict["is_reviewer"] = user_is_resource_reviewer(user)
            userDict["viewable_nodegroups"] = user.userprofile.viewable_nodegroups
            userDict["editable_nodegroups"] = user.userprofile.editable_nodegroups
            userDict["deletable_nodegroups"] = user.userprofile.deletable_nodegroups
            response = JSONResponse(userDict)
        else:
            response = Http401Response()

        return response
Exemplo n.º 17
0
 def post(self, request):
     username = request.POST.get("username", None)
     password = request.POST.get("password", None)
     user = authenticate(username=username, password=password)
     if settings.MOBILE_OAUTH_CLIENT_ID == "":
         message = _("Make sure to set your MOBILE_OAUTH_CLIENT_ID in settings.py")
         response = HttpResponse(message, status=500)
         logger.warning(message)
     else:
         if user:
             if hasattr(user, "userprofile") is not True:
                 models.UserProfile.objects.create(user=user)
             is_reviewer = user_is_resource_reviewer(user)
             user = JSONSerializer().serializeToPython(user)
             user["password"] = None
             user["is_reviewer"] = is_reviewer
             response = JSONResponse({"user": user, "clientid": settings.MOBILE_OAUTH_CLIENT_ID})
         else:
             response = Http401Response()
     return response
Exemplo n.º 18
0
    def get(self, request, resourceid):
        try:
            resource_instance = Resource.objects.get(pk=resourceid)
            graph = resource_instance.graph
        except Resource.DoesNotExist:
            graph = models.GraphModel.objects.get(pk=resourceid)
            resourceid = None
            resource_instance = None
            pass
        nodes = graph.node_set.all()

        nodegroups = []
        editable_nodegroups = []
        for node in nodes:
            if node.is_collector:
                added = False
                if request.user.has_perm("write_nodegroup", node.nodegroup):
                    editable_nodegroups.append(node.nodegroup)
                    nodegroups.append(node.nodegroup)
                    added = True
                if not added and request.user.has_perm("read_nodegroup", node.nodegroup):
                    nodegroups.append(node.nodegroup)

        nodes = nodes.filter(nodegroup__in=nodegroups)
        cards = graph.cardmodel_set.order_by("sortorder").filter(nodegroup__in=nodegroups).prefetch_related("cardxnodexwidget_set")
        cardwidgets = [
            widget for widgets in [card.cardxnodexwidget_set.order_by("sortorder").all() for card in cards] for widget in widgets
        ]
        datatypes = models.DDataType.objects.all()
        user_is_reviewer = user_is_resource_reviewer(request.user)
        widgets = models.Widget.objects.all()
        card_components = models.CardComponent.objects.all()

        if resource_instance is None:
            tiles = []
            displayname = _("New Resource")
        else:
            displayname = resource_instance.displayname
            if displayname == "undefined":
                displayname = _("Unnamed Resource")
            if str(resource_instance.graph_id) == settings.SYSTEM_SETTINGS_RESOURCE_MODEL_ID:
                displayname = _("System Settings")

            tiles = resource_instance.tilemodel_set.order_by("sortorder").filter(nodegroup__in=nodegroups)
            provisionaltiles = []
            for tile in tiles:
                append_tile = True
                isfullyprovisional = False
                if tile.provisionaledits is not None:
                    if len(list(tile.provisionaledits.keys())) > 0:
                        if len(tile.data) == 0:
                            isfullyprovisional = True
                        if user_is_reviewer is False:
                            if str(request.user.id) in tile.provisionaledits:
                                tile.provisionaledits = {str(request.user.id): tile.provisionaledits[str(request.user.id)]}
                                tile.data = tile.provisionaledits[str(request.user.id)]["value"]
                            else:
                                if isfullyprovisional is True:
                                    # if the tile IS fully provisional and the current user is not the owner,
                                    # we don't send that tile back to the client.
                                    append_tile = False
                                else:
                                    # if the tile has authoritaive data and the current user is not the owner,
                                    # we don't send the provisional data of other users back to the client.
                                    tile.provisionaledits = None
                if append_tile is True:
                    provisionaltiles.append(tile)
            tiles = provisionaltiles

        cards = JSONSerializer().serializeToPython(cards)
        editable_nodegroup_ids = [str(nodegroup.pk) for nodegroup in editable_nodegroups]
        for card in cards:
            card["is_writable"] = False
            if str(card["nodegroup_id"]) in editable_nodegroup_ids:
                card["is_writable"] = True

        context = {
            "resourceid": resourceid,
            "displayname": displayname,
            "tiles": tiles,
            "cards": cards,
            "nodegroups": nodegroups,
            "nodes": nodes,
            "cardwidgets": cardwidgets,
            "datatypes": datatypes,
            "userisreviewer": user_is_reviewer,
            "widgets": widgets,
            "card_components": card_components,
        }

        return JSONResponse(context, indent=4)
Exemplo n.º 19
0
def search_results(request, returnDsl=False):
    for_export = request.GET.get("export")
    pages = request.GET.get("pages", None)
    total = int(request.GET.get("total", "0"))
    resourceinstanceid = request.GET.get("id", None)
    load_tiles = request.GET.get("tiles", False)
    if load_tiles:
        try:
            load_tiles = json.loads(load_tiles)
        except TypeError:
            pass
    se = SearchEngineFactory().create()
    permitted_nodegroups = get_permitted_nodegroups(request.user)
    include_provisional = get_provisional_type(request)
    search_filter_factory = SearchFilterFactory(request)
    search_results_object = {"query": Query(se)}

    try:
        for filter_type, querystring in list(
                request.GET.items()) + [("search-results", "")]:
            search_filter = search_filter_factory.get_filter(filter_type)
            if search_filter:
                search_filter.append_dsl(search_results_object,
                                         permitted_nodegroups,
                                         include_provisional)
        append_instance_permission_filter_dsl(request, search_results_object)
    except Exception as err:
        logger.exception(err)
        return JSONErrorResponse(message=err)

    dsl = search_results_object.pop("query", None)
    if returnDsl:
        return dsl
    dsl.include("graph_id")
    dsl.include("root_ontology_class")
    dsl.include("resourceinstanceid")
    dsl.include("points")
    dsl.include("permissions.users_without_read_perm")
    dsl.include("permissions.users_without_edit_perm")
    dsl.include("permissions.users_without_delete_perm")
    dsl.include("permissions.users_with_no_access")
    dsl.include("geometries")
    dsl.include("displayname")
    dsl.include("displaydescription")
    dsl.include("map_popup")
    dsl.include("provisional_resource")
    if load_tiles:
        dsl.include("tiles")
    if for_export or pages:
        results = dsl.search(index=RESOURCES_INDEX, scroll="1m")
        scroll_id = results["_scroll_id"]
        if not pages:
            if total <= settings.SEARCH_EXPORT_LIMIT:
                pages = (total // settings.SEARCH_RESULT_LIMIT) + 1
            if total > settings.SEARCH_EXPORT_LIMIT:
                pages = int(settings.SEARCH_EXPORT_LIMIT //
                            settings.SEARCH_RESULT_LIMIT) - 1
        for page in range(int(pages)):
            results_scrolled = dsl.se.es.scroll(scroll_id=scroll_id,
                                                scroll="1m")
            results["hits"]["hits"] += results_scrolled["hits"]["hits"]
    else:
        results = dsl.search(index=RESOURCES_INDEX, id=resourceinstanceid)

    ret = {}
    if results is not None:
        if "hits" not in results:
            if "docs" in results:
                results = {"hits": {"hits": results["docs"]}}
            else:
                results = {"hits": {"hits": [results]}}

        # allow filters to modify the results
        for filter_type, querystring in list(
                request.GET.items()) + [("search-results", "")]:
            search_filter = search_filter_factory.get_filter(filter_type)
            if search_filter:
                search_filter.post_search_hook(search_results_object, results,
                                               permitted_nodegroups)

        ret["results"] = results

        for key, value in list(search_results_object.items()):
            ret[key] = value

        ret["reviewer"] = user_is_resource_reviewer(request.user)
        ret["timestamp"] = datetime.now()
        ret["total_results"] = dsl.count(index=RESOURCES_INDEX)
        ret["userid"] = request.user.id
        return JSONResponse(ret)

    else:
        ret = {
            "message": _("There was an error retrieving the search results")
        }
        return JSONResponse(ret, status=500)
Exemplo n.º 20
0
    def get(
        self,
        request,
        graphid=None,
        resourceid=None,
        view_template="views/resource/editor.htm",
        main_script="views/resource/editor",
        nav_menu=True,
    ):
        if self.action == "copy":
            return self.copy(request, resourceid)

        creator = None
        user_created_instance = None

        if resourceid is None:
            resource_instance = None
            graph = models.GraphModel.objects.get(pk=graphid)
            resourceid = ""
        else:
            resource_instance = Resource.objects.get(pk=resourceid)
            graph = resource_instance.graph
            creator, user_created_instance = get_instance_creator(resource_instance, request.user)
        nodes = graph.node_set.all()
        resource_graphs = (
            models.GraphModel.objects.exclude(pk=settings.SYSTEM_SETTINGS_RESOURCE_MODEL_ID)
            .exclude(isresource=False)
            .exclude(isactive=False)
        )
        ontologyclass = [node for node in nodes if node.istopnode is True][0].ontologyclass
        relationship_type_values = get_resource_relationship_types()

        nodegroups = []
        editable_nodegroups = []
        for node in nodes:
            if node.is_collector:
                added = False
                if request.user.has_perm("write_nodegroup", node.nodegroup):
                    editable_nodegroups.append(node.nodegroup)
                    nodegroups.append(node.nodegroup)
                    added = True
                if not added and request.user.has_perm("read_nodegroup", node.nodegroup):
                    nodegroups.append(node.nodegroup)

        nodes = nodes.filter(nodegroup__in=nodegroups)
        cards = graph.cardmodel_set.order_by("sortorder").filter(nodegroup__in=nodegroups).prefetch_related("cardxnodexwidget_set")
        cardwidgets = [
            widget for widgets in [card.cardxnodexwidget_set.order_by("sortorder").all() for card in cards] for widget in widgets
        ]
        widgets = models.Widget.objects.all()
        card_components = models.CardComponent.objects.all()
        applied_functions = JSONSerializer().serialize(models.FunctionXGraph.objects.filter(graph=graph))
        datatypes = models.DDataType.objects.all()
        user_is_reviewer = user_is_resource_reviewer(request.user)
        is_system_settings = False

        if resource_instance is None:
            tiles = []
            displayname = _("New Resource")
        else:
            displayname = resource_instance.displayname
            if displayname == "undefined":
                displayname = _("Unnamed Resource")
            if str(resource_instance.graph_id) == settings.SYSTEM_SETTINGS_RESOURCE_MODEL_ID:
                is_system_settings = True
                displayname = _("System Settings")

            tiles = resource_instance.tilemodel_set.order_by("sortorder").filter(nodegroup__in=nodegroups)
            provisionaltiles = []
            for tile in tiles:
                append_tile = True
                isfullyprovisional = False
                if tile.provisionaledits is not None:
                    if len(list(tile.provisionaledits.keys())) > 0:
                        if len(tile.data) == 0:
                            isfullyprovisional = True
                        if user_is_reviewer is False:
                            if str(request.user.id) in tile.provisionaledits:
                                tile.provisionaledits = {str(request.user.id): tile.provisionaledits[str(request.user.id)]}
                                tile.data = tile.provisionaledits[str(request.user.id)]["value"]
                            else:
                                if isfullyprovisional is True:
                                    # if the tile IS fully provisional and the current user is not the owner,
                                    # we don't send that tile back to the client.
                                    append_tile = False
                                else:
                                    # if the tile has authoritaive data and the current user is not the owner,
                                    # we don't send the provisional data of other users back to the client.
                                    tile.provisionaledits = None
                if append_tile is True:
                    provisionaltiles.append(tile)
            tiles = provisionaltiles
        map_layers = models.MapLayer.objects.all()
        map_markers = models.MapMarker.objects.all()
        map_sources = models.MapSource.objects.all()
        geocoding_providers = models.Geocoder.objects.all()
        templates = models.ReportTemplate.objects.all()

        cards = JSONSerializer().serializeToPython(cards)
        editable_nodegroup_ids = [str(nodegroup.pk) for nodegroup in editable_nodegroups]
        for card in cards:
            card["is_writable"] = False
            if str(card["nodegroup_id"]) in editable_nodegroup_ids:
                card["is_writable"] = True

        context = self.get_context_data(
            main_script=main_script,
            resourceid=resourceid,
            displayname=displayname,
            graphid=graph.graphid,
            graphiconclass=graph.iconclass,
            graphname=graph.name,
            ontologyclass=ontologyclass,
            resource_graphs=resource_graphs,
            relationship_types=relationship_type_values,
            widgets=widgets,
            widgets_json=JSONSerializer().serialize(widgets),
            card_components=card_components,
            card_components_json=JSONSerializer().serialize(card_components),
            tiles=JSONSerializer().serialize(tiles),
            cards=JSONSerializer().serialize(cards),
            applied_functions=applied_functions,
            nodegroups=JSONSerializer().serialize(nodegroups),
            nodes=JSONSerializer().serialize(nodes),
            cardwidgets=JSONSerializer().serialize(cardwidgets),
            datatypes_json=JSONSerializer().serialize(datatypes, exclude=["iconclass", "modulename", "classname"]),
            map_layers=map_layers,
            map_markers=map_markers,
            map_sources=map_sources,
            geocoding_providers=geocoding_providers,
            user_is_reviewer=json.dumps(user_is_reviewer),
            creator=json.dumps(creator),
            user_created_instance=json.dumps(user_created_instance),
            report_templates=templates,
            templates_json=JSONSerializer().serialize(templates, sort_keys=False, exclude=["name", "description"]),
            graph_json=JSONSerializer().serialize(graph),
            is_system_settings=is_system_settings,
        )

        context["nav"]["title"] = ""
        context["nav"]["menu"] = nav_menu
        if resourceid == settings.RESOURCE_INSTANCE_ID:
            context["nav"]["help"] = {"title": _("Managing System Settings"), "template": "system-settings-help"}
        else:
            context["nav"]["help"] = {"title": _("Using the Resource Editor"), "template": "resource-editor-help"}

        return render(request, view_template, context)
Exemplo n.º 21
0
def search_results(request):
    for_export = request.GET.get("export")
    total = int(request.GET.get("total", "0"))
    se = SearchEngineFactory().create()
    search_results_object = {"query": Query(se)}

    include_provisional = get_provisional_type(request)
    permitted_nodegroups = get_permitted_nodegroups(request.user)

    search_filter_factory = SearchFilterFactory(request)
    try:
        for filter_type, querystring in list(
                request.GET.items()) + [("search-results", "")]:
            search_filter = search_filter_factory.get_filter(filter_type)
            if search_filter:
                search_filter.append_dsl(search_results_object,
                                         permitted_nodegroups,
                                         include_provisional)
    except Exception as err:
        return JSONErrorResponse(message=err)

    dsl = search_results_object.pop("query", None)
    dsl.include("graph_id")
    dsl.include("root_ontology_class")
    dsl.include("resourceinstanceid")
    dsl.include("points")
    dsl.include("geometries")
    dsl.include("displayname")
    dsl.include("displaydescription")
    dsl.include("map_popup")
    dsl.include("provisional_resource")
    if request.GET.get("tiles", None) is not None:
        dsl.include("tiles")

    if for_export is True:
        results = dsl.search(index="resources", scroll="1m")
        scroll_id = results["_scroll_id"]

        if total <= settings.SEARCH_EXPORT_LIMIT:
            pages = (total // settings.SEARCH_RESULT_LIMIT) + 1
        if total > settings.SEARCH_EXPORT_LIMIT:
            pages = int(settings.SEARCH_EXPORT_LIMIT //
                        settings.SEARCH_RESULT_LIMIT) - 1
        for page in range(pages):
            results_scrolled = dsl.se.es.scroll(scroll_id=scroll_id,
                                                scroll="1m")
            results["hits"]["hits"] += results_scrolled["hits"]["hits"]
    else:
        results = dsl.search(index="resources")

    ret = {}
    if results is not None:
        # allow filters to modify the results
        for filter_type, querystring in list(
                request.GET.items()) + [("search-results", "")]:
            search_filter = search_filter_factory.get_filter(filter_type)
            if search_filter:
                search_filter.post_search_hook(search_results_object, results,
                                               permitted_nodegroups)

        ret["results"] = results

        for key, value in list(search_results_object.items()):
            ret[key] = value

        ret["reviewer"] = user_is_resource_reviewer(request.user)
        ret["timestamp"] = datetime.now()
        ret["total_results"] = dsl.count(index="resources")

        return JSONResponse(ret)

    else:
        ret = {
            "message": _("There was an error retrieving the search results")
        }
        return JSONResponse(ret, status=500)
Exemplo n.º 22
0
def search_terms(request):
    lang = request.GET.get("lang", settings.LANGUAGE_CODE)
    se = SearchEngineFactory().create()
    searchString = request.GET.get("q", "")
    user_is_reviewer = user_is_resource_reviewer(request.user)

    i = 0
    ret = {}
    for index in ["terms", "concepts"]:
        query = Query(se, start=0, limit=0)
        boolquery = Bool()
        boolquery.should(
            Match(field="value",
                  query=searchString.lower(),
                  type="phrase_prefix"))
        boolquery.should(
            Match(field="value.folded",
                  query=searchString.lower(),
                  type="phrase_prefix"))
        boolquery.should(
            Match(field="value.folded",
                  query=searchString.lower(),
                  fuzziness="AUTO",
                  prefix_length=settings.SEARCH_TERM_SENSITIVITY))

        if user_is_reviewer is False and index == "terms":
            boolquery.filter(Terms(field="provisional", terms=["false"]))

        query.add_query(boolquery)
        base_agg = Aggregation(name="value_agg",
                               type="terms",
                               field="value.raw",
                               size=settings.SEARCH_DROPDOWN_LENGTH,
                               order={"max_score": "desc"})
        nodegroupid_agg = Aggregation(name="nodegroupid",
                                      type="terms",
                                      field="nodegroupid")
        top_concept_agg = Aggregation(name="top_concept",
                                      type="terms",
                                      field="top_concept")
        conceptid_agg = Aggregation(name="conceptid",
                                    type="terms",
                                    field="conceptid")
        max_score_agg = MaxAgg(name="max_score", script="_score")

        top_concept_agg.add_aggregation(conceptid_agg)
        base_agg.add_aggregation(max_score_agg)
        base_agg.add_aggregation(top_concept_agg)
        base_agg.add_aggregation(nodegroupid_agg)
        query.add_aggregation(base_agg)

        ret[index] = []
        results = query.search(index=index)
        if results is not None:
            for result in results["aggregations"]["value_agg"]["buckets"]:
                if len(result["top_concept"]["buckets"]) > 0:
                    for top_concept in result["top_concept"]["buckets"]:
                        top_concept_id = top_concept["key"]
                        top_concept_label = get_preflabel_from_conceptid(
                            top_concept["key"], lang)["value"]
                        for concept in top_concept["conceptid"]["buckets"]:
                            ret[index].append({
                                "type": "concept",
                                "context": top_concept_id,
                                "context_label": top_concept_label,
                                "id": i,
                                "text": result["key"],
                                "value": concept["key"],
                            })
                        i = i + 1
                else:
                    ret[index].append({
                        "type":
                        "term",
                        "context":
                        "",
                        "context_label":
                        get_resource_model_label(result),
                        "id":
                        i,
                        "text":
                        result["key"],
                        "value":
                        result["key"],
                    })
                    i = i + 1

    return JSONResponse(ret)
Exemplo n.º 23
0
    def get(
        self,
        request,
        graphid=None,
        resourceid=None,
        view_template="views/resource/editor.htm",
        main_script="views/resource/editor",
        nav_menu=True,
    ):
        if self.action == "copy":
            return self.copy(request, resourceid)

        resource_instance_exists = False

        try:
            resource_instance = Resource.objects.get(pk=resourceid)
            resource_instance_exists = True
            graphid = resource_instance.graph_id

        except ObjectDoesNotExist:
            resource_instance = Resource()
            resource_instance.resourceinstanceid = resourceid
            resource_instance.graph_id = graphid

        if resourceid is not None:
            resource_graphs = (models.GraphModel.objects.exclude(
                pk=settings.SYSTEM_SETTINGS_RESOURCE_MODEL_ID).exclude(
                    isresource=False).exclude(isactive=False))
            graph = Graph.objects.get(graphid=graphid)
            relationship_type_values = get_resource_relationship_types()
            datatypes = models.DDataType.objects.all()
            widgets = models.Widget.objects.all()
            map_layers = models.MapLayer.objects.all()
            map_markers = models.MapMarker.objects.all()
            map_sources = models.MapSource.objects.all()
            geocoding_providers = models.Geocoder.objects.all()
            required_widgets = []

            widget_datatypes = [v.datatype for k, v in graph.nodes.items()]
            widgets = widgets.filter(datatype__in=widget_datatypes)

            if resource_instance_exists == True:
                displayname = Resource.objects.get(pk=resourceid).displayname
                if displayname == "undefined":
                    displayname = "Unnamed Resource"
            else:
                displayname = "Unnamed Resource"

            date_nodes = models.Node.objects.filter(datatype="date",
                                                    graph__isresource=True,
                                                    graph__isactive=True)
            searchable_datatypes = [
                d.pk
                for d in models.DDataType.objects.filter(issearchable=True)
            ]
            searchable_nodes = models.Node.objects.filter(
                graph__isresource=True,
                graph__isactive=True,
                datatype__in=searchable_datatypes,
                issearchable=True)
            resource_cards = models.CardModel.objects.filter(
                graph__isresource=True, graph__isactive=True)
            context = self.get_context_data(
                main_script=main_script,
                resource_type=graph.name,
                relationship_types=relationship_type_values,
                iconclass=graph.iconclass,
                datatypes_json=JSONSerializer().serialize(
                    datatypes,
                    exclude=["iconclass", "modulename", "classname"]),
                datatypes=datatypes,
                widgets=widgets,
                date_nodes=date_nodes,
                map_layers=map_layers,
                map_markers=map_markers,
                map_sources=map_sources,
                geocoding_providers=geocoding_providers,
                widgets_json=JSONSerializer().serialize(widgets),
                resourceid=resourceid,
                resource_graphs=resource_graphs,
                graph_json=JSONSerializer().serialize(
                    graph,
                    exclude=[
                        "iconclass",
                        "functions",
                        "functions_x_graphs",
                        "name",
                        "description",
                        "deploymentfile",
                        "author",
                        "deploymentdate",
                        "version",
                        "isresource",
                        "isactive",
                        "iconclass",
                        "ontology",
                    ],
                ),
                displayname=displayname,
                resource_cards=JSONSerializer().serialize(resource_cards,
                                                          exclude=[
                                                              "description",
                                                              "instructions",
                                                              "active",
                                                              "isvisible"
                                                          ]),
                searchable_nodes=JSONSerializer().serialize(
                    searchable_nodes,
                    exclude=[
                        "description", "ontologyclass", "isrequired",
                        "issearchable", "istopnode"
                    ]),
                saved_searches=JSONSerializer().serialize(
                    settings.SAVED_SEARCHES),
                resource_instance_exists=resource_instance_exists,
                user_is_reviewer=json.dumps(
                    user_is_resource_reviewer(request.user)),
                userid=request.user.id,
            )

            if graph.iconclass:
                context["nav"]["icon"] = graph.iconclass
            context["nav"]["title"] = graph.name
            context["nav"]["menu"] = nav_menu
            if resourceid == settings.RESOURCE_INSTANCE_ID:
                context["nav"]["help"] = (_("Managing System Settings"),
                                          "help/base-help.htm")
                context["help"] = "system-settings-help"
            else:
                context["nav"]["help"] = (_("Using the Resource Editor"),
                                          "help/base-help.htm")
                context["help"] = "resource-editor-help"

            return render(request, view_template, context)

        return HttpResponseNotFound()
Exemplo n.º 24
0
    def save(self, *args, **kwargs):
        request = kwargs.pop("request", None)
        index = kwargs.pop("index", True)
        user = kwargs.pop("user", None)
        log = kwargs.pop("log", True)
        provisional_edit_log_details = kwargs.pop("provisional_edit_log_details", None)
        missing_nodes = []
        creating_new_tile = True
        user_is_reviewer = False
        newprovisionalvalue = None
        oldprovisionalvalue = None

        try:
            if user is None and request is not None:
                user = request.user
            user_is_reviewer = user_is_resource_reviewer(user)
        except AttributeError:  # no user - probably importing data
            user = None


        # if user is not None:
        #     self.validate([])

        with transaction.atomic():
            for nodeid, value in self.data.items():
                node = models.Node.objects.get(nodeid=nodeid)
                datatype = self.datatype_factory.get_instance(node.datatype)
                datatype.pre_tile_save(self, nodeid)
            self.__preSave(request)
            self.check_for_missing_nodes(request)
            self.check_for_constraint_violation(request)

            creating_new_tile = models.TileModel.objects.filter(pk=self.tileid).exists() is False
            edit_type = "tile create" if (creating_new_tile is True) else "tile edit"

            if creating_new_tile is False:
                existing_model = models.TileModel.objects.get(pk=self.tileid)

            # this section moves the data over from self.data to self.provisionaledits if certain users permissions are in force
            # then self.data is restored from the previously saved tile data
            if user is not None and user_is_reviewer is False:
                if creating_new_tile is True:
                    self.apply_provisional_edit(user, data=self.data, action="create")
                    newprovisionalvalue = self.data
                    self.data = {}

                else:
                    # the user has previously edited this tile
                    self.apply_provisional_edit(user, self.data, action="update", existing_model=existing_model)
                    newprovisionalvalue = self.data
                    self.data = existing_model.data

                    oldprovisional = self.get_provisional_edit(existing_model, user)
                    if oldprovisional is not None:
                        oldprovisionalvalue = oldprovisional["value"]

                if provisional_edit_log_details is None:
                    provisional_edit_log_details = {
                        "user": user,
                        "provisional_editor": user,
                        "action": "create tile" if creating_new_tile else "add edit",
                    }

            if user is not None:
                self.validate([])

            super(Tile, self).save(*args, **kwargs)
            # We have to save the edit log record after calling save so that the
            # resource's displayname changes are avaliable
            user = {} if user is None else user
            self.datatype_post_save_actions(request)
            self.__postSave(request)
            if creating_new_tile is True:
                self.save_edit(
                    user=user,
                    edit_type=edit_type,
                    old_value={},
                    new_value=self.data,
                    newprovisionalvalue=newprovisionalvalue,
                    provisional_edit_log_details=provisional_edit_log_details,
                )
            else:
                self.save_edit(
                    user=user,
                    edit_type=edit_type,
                    old_value=existing_model.data,
                    new_value=self.data,
                    newprovisionalvalue=newprovisionalvalue,
                    oldprovisionalvalue=oldprovisionalvalue,
                    provisional_edit_log_details=provisional_edit_log_details,
                )

            if index:
                self.index()

            for tile in self.tiles:
                tile.resourceinstance = self.resourceinstance
                tile.parenttile = self
                tile.save(*args, request=request, index=index, **kwargs)
Exemplo n.º 25
0
    def save(self, *args, **kwargs):
        request = kwargs.pop("request", None)
        index = kwargs.pop("index", True)
        user = kwargs.pop("user", None)
        log = kwargs.pop("log", True)
        provisional_edit_log_details = kwargs.pop(
            "provisional_edit_log_details", None)
        self.__preSave(request)
        missing_nodes = []
        creating_new_tile = True
        user_is_reviewer = False
        newprovisionalvalue = None
        oldprovisionalvalue = None
        self.check_for_missing_nodes(request)
        self.check_for_constraint_violation(request)

        try:
            if user is None and request is not None:
                user = request.user
            user_is_reviewer = user_is_resource_reviewer(user)
        except AttributeError:  # no user - probably importing data
            user = None

        creating_new_tile = models.TileModel.objects.filter(
            pk=self.tileid).exists() is False
        edit_type = "tile create" if (
            creating_new_tile is True) else "tile edit"

        if creating_new_tile is False:
            existing_model = models.TileModel.objects.get(pk=self.tileid)

        if user is not None:
            if user_is_reviewer is False and creating_new_tile is False:
                self.apply_provisional_edit(user,
                                            self.data,
                                            action="update",
                                            existing_model=existing_model)
                newprovisionalvalue = self.data
                oldprovisional = self.get_provisional_edit(
                    existing_model, user)
                if oldprovisional is not None:
                    oldprovisionalvalue = oldprovisional["value"]

                self.data = existing_model.data
                if provisional_edit_log_details is None:
                    provisional_edit_log_details = {
                        "user": user,
                        "action": "add edit",
                        "provisional_editor": user
                    }

            if creating_new_tile is True:
                if self.is_provisional(
                ) is False and user_is_reviewer is False:
                    self.apply_provisional_edit(user,
                                                data=self.data,
                                                action="create")
                    newprovisionalvalue = self.data
                    self.data = {}
                    if provisional_edit_log_details is None:
                        provisional_edit_log_details = {
                            "user": user,
                            "action": "create tile",
                            "provisional_editor": user
                        }

        if user is not None:
            self.validate([])

        super(Tile, self).save(*args, **kwargs)
        # We have to save the edit log record after calling save so that the
        # resource's displayname changes are avaliable
        if log is True:
            user = {} if user is None else user
            self.datatype_post_save_actions(request)
            if creating_new_tile is True:
                self.save_edit(
                    user=user,
                    edit_type=edit_type,
                    old_value={},
                    new_value=self.data,
                    newprovisionalvalue=newprovisionalvalue,
                    provisional_edit_log_details=provisional_edit_log_details,
                )
            else:
                self.save_edit(
                    user=user,
                    edit_type=edit_type,
                    old_value=existing_model.data,
                    new_value=self.data,
                    newprovisionalvalue=newprovisionalvalue,
                    oldprovisionalvalue=oldprovisionalvalue,
                    provisional_edit_log_details=provisional_edit_log_details,
                )

        if index:
            self.index()

        for tile in self.tiles:
            tile.resourceinstance = self.resourceinstance
            tile.parenttile = self
            tile.save(*args, request=request, index=index, **kwargs)