예제 #1
0
def delete(map_identifier, topic_identifier, file_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    file_occurrence = topic_store.get_occurrence(
        map_identifier,
        file_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    form_file_title = file_occurrence.get_attribute_by_name("title").value
    form_file_scope = file_occurrence.scope

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]

    if request.method == "POST":
        # Delete file occurrence from topic store
        topic_store.delete_occurrence(map_identifier,
                                      file_occurrence.identifier)

        # Delete file from file system
        file_file_path = os.path.join(
            current_app.static_folder,
            RESOURCES_DIRECTORY,
            str(map_identifier),
            file_occurrence.resource_ref,
        )
        if os.path.exists(file_file_path):
            os.remove(file_file_path)

        flash("3D content successfully deleted.", "warning")
        return redirect(
            url_for(
                "three_d.index",
                map_identifier=topic_map.identifier,
                topic_identifier=topic.identifier,
            ))

    return render_template(
        "three_d/delete.html",
        topic_map=topic_map,
        topic=topic,
        file_identifier=file_occurrence.identifier,
        file_title=form_file_title,
        file_scope=form_file_scope,
        map_notes_count=map_notes_count,
    )
예제 #2
0
def upload(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_topic_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    # If the map doesn't belong to the user and they don't have the right collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    form_image_title = ""
    form_image_scope = session["current_scope"]

    error = 0

    if request.method == "POST":
        form_image_title = request.form["image-title"].strip()
        form_image_scope = request.form["image-scope"].strip()
        form_upload_file = request.files[
            "image-file"] if "image-file" in request.files else None

        # If no values have been provided set their default values
        if not form_image_scope:
            form_image_scope = UNIVERSAL_SCOPE

        # Validate form inputs
        if not form_image_title:
            error = error | 1
        if not form_upload_file:
            error = error | 2
        else:
            if form_upload_file.filename == "":
                error = error | 4
            elif not allowed_file(form_upload_file.filename):
                error = error | 8
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_image_scope):
            error = error | 16

        if error != 0:
            flash(
                "An error occurred when uploading the image. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            image_file_name = f"{str(uuid.uuid4())}.{get_file_extension(form_upload_file.filename)}"

            # Create the image directory for this topic map and topic if it doesn't already exist
            image_directory = os.path.join(bp.root_path, RESOURCES_DIRECTORY,
                                           str(map_identifier),
                                           topic_identifier)
            if not os.path.isdir(image_directory):
                os.makedirs(image_directory)

            file_path = os.path.join(image_directory, image_file_name)
            form_upload_file.save(file_path)

            image_occurrence = Occurrence(
                instance_of="image",
                topic_identifier=topic.identifier,
                scope=form_image_scope,
                resource_ref=image_file_name,
            )
            title_attribute = Attribute(
                "title",
                form_image_title,
                image_occurrence.identifier,
                data_type=DataType.STRING,
            )

            # Persist objects to the topic store
            topic_store.set_occurrence(topic_map.identifier, image_occurrence)
            topic_store.set_attribute(topic_map.identifier, title_attribute)

            flash("Image successfully uploaded.", "success")
            return redirect(
                url_for(
                    "image.index",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "image/upload.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        image_title=form_image_title,
        image_scope=form_image_scope,
    )
예제 #3
0
def edit(map_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_topic_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    if not topic_map.owner:
        abort(403)

    form_map_name = topic_map.name
    form_map_description = topic_map.description
    form_map_published = topic_map.published

    error = 0

    if request.method == "POST":
        form_map_name = request.form["map-name"].strip()
        form_map_description = request.form["map-description"].strip()
        form_map_published = True if request.form.get("map-published") == "1" else False
        form_upload_file = request.files["map-image-file"] if "map-image-file" in request.files else None

        # Validate form inputs
        if not form_map_name:
            error = error | 1
        if form_upload_file:
            if form_upload_file.filename == "":
                error = error | 2
            elif not allowed_file(form_upload_file.filename):
                error = error | 4

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            if form_upload_file:
                # Upload the image for the topic map to the map's directory
                image_file_name = f"{str(uuid.uuid4())}.{get_file_extension(form_upload_file.filename)}"
                topic_map_directory = os.path.join(bp.root_path, RESOURCES_DIRECTORY, str(map_identifier))
                file_path = os.path.join(topic_map_directory, image_file_name)
                form_upload_file.save(file_path)

                # TODO: Remove the map's previous image
            else:
                image_file_name = topic_map.image_path

            # Update the topic map
            promoted = form_map_published and topic_map.promoted
            topic_store.update_topic_map(
                map_identifier,
                form_map_name,
                form_map_description,
                image_file_name,
                published=form_map_published,
                promoted=promoted,
            )

            flash("Map successfully updated.", "success")
        return redirect(url_for("map.view", map_identifier=map_identifier))

    return render_template(
        "map/edit.html",
        error=error,
        topic_map=topic_map,
        map_name=form_map_name,
        map_description=form_map_description,
        map_shared=form_map_published,
    )
예제 #4
0
def delete_reference(
    map_identifier,
    topic_identifier,
    association_identifier,
    member_identifier,
    reference_identifier,
):
    topic_store = get_topic_store()

    topic_map = topic_store.get_topic_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    association = topic_store.get_association(map_identifier,
                                              association_identifier)
    if association is None:
        abort(404)

    member = association.get_member(member_identifier)

    form_topic_reference = reference_identifier

    if request.method == "POST":
        if len(member.topic_refs) > 1:
            member.remove_topic_ref(form_topic_reference)
            topic_store.delete_association(map_identifier,
                                           association_identifier)
            topic_store.set_association(map_identifier, association)

            flash("Topic reference successfully deleted.", "warning")
        else:
            flash("Topic reference was not deleted.", "warning")
        return redirect(
            url_for(
                "association.view_member",
                map_identifier=topic_map.identifier,
                topic_identifier=topic.identifier,
                association_identifier=association_identifier,
                member_identifier=member_identifier,
            ))

    return render_template(
        "association/delete_reference.html",
        topic_map=topic_map,
        topic=topic,
        association=association,
        member=member,
        topic_reference=form_topic_reference,
    )
예제 #5
0
def edit(map_identifier, topic_identifier, image_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_topic_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    # If the map doesn't belong to the user and they don't have the right collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    image_occurrence = topic_store.get_occurrence(
        map_identifier,
        image_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    form_image_title = image_occurrence.get_attribute_by_name("title").value
    form_image_resource_ref = image_occurrence.resource_ref
    form_image_scope = image_occurrence.scope

    error = 0

    if request.method == "POST":
        form_image_title = request.form["image-title"].strip()
        form_image_scope = request.form["image-scope"].strip()

        # If no values have been provided set their default values
        if not form_image_scope:
            form_image_scope = UNIVERSAL_SCOPE

        # Validate form inputs
        if not form_image_title:
            error = error | 1
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_image_scope):
            error = error | 2

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            # Update image's title if it has changed
            if image_occurrence.get_attribute_by_name(
                    "title").value != form_image_title:
                topic_store.update_attribute_value(
                    topic_map.identifier,
                    image_occurrence.get_attribute_by_name("title").identifier,
                    form_image_title,
                )

            # Update image's scope if it has changed
            if image_occurrence.scope != form_image_scope:
                topic_store.update_occurrence_scope(
                    map_identifier, image_occurrence.identifier,
                    form_image_scope)

            flash("Image successfully updated.", "success")
            return redirect(
                url_for(
                    "image.index",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "image/edit.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        image_identifier=image_occurrence.identifier,
        image_title=form_image_title,
        image_resource_ref=form_image_resource_ref,
        image_scope=form_image_scope,
    )
예제 #6
0
def entity_edit(
    map_identifier,
    topic_identifier,
    entity_identifier,
    attribute_identifier,
    entity_type,
):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)
    if "admin" not in current_user.roles:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    entity = topic_store.get_association(
        map_identifier,
        entity_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if entity is None:
        entity = topic_store.get_occurrence(map_identifier, entity_identifier)

    if entity is None:
        abort(404)

    attribute = topic_store.get_attribute(map_identifier, attribute_identifier)
    if attribute is None:
        abort(404)

    form_attribute_name = attribute.name
    form_attribute_value = attribute.value
    form_attribute_type = str(attribute.data_type).capitalize()
    form_attribute_scope = attribute.scope

    error = 0

    if request.method == "POST":
        form_attribute_name = request.form["attribute-name"].strip()
        form_attribute_value = request.form["attribute-value"].strip()
        form_attribute_type = request.form["attribute-type"]
        form_attribute_scope = request.form["attribute-scope"].strip()

        # If no values have been provided set their default values
        if not form_attribute_scope:
            form_attribute_scope = "*"  # Universal scope

        # Validate form inputs
        if not form_attribute_name:
            error = error | 1
        if not form_attribute_value:
            error = error | 2
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_attribute_scope):
            error = error | 4

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            # Update the attribute by deleting the existing attribute and adding a new one
            topic_store.delete_attribute(map_identifier, attribute.identifier)
            updated_attribute = Attribute(
                form_attribute_name,
                form_attribute_value,
                entity.identifier,
                data_type=DataType[form_attribute_type],
            )
            topic_store.set_attribute(topic_map.identifier, updated_attribute)

            flash("Attribute successfully updated.", "success")
            return redirect(
                url_for(
                    "attribute.entity_index",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                    entity_identifier=entity.identifier,
                    entity_type=entity_type,
                ))

    data_types = [
        ("STRING", "String"),
        ("NUMBER", "Number"),
        ("TIMESTAMP", "Timestamp"),
        ("BOOLEAN", "Boolean"),
    ]
    post_url = "attribute.entity_edit"
    cancel_url = "attribute.entity_index"

    return render_template(
        "attribute/edit.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        attribute=attribute,
        entity=entity,
        entity_type=entity_type,
        data_types=data_types,
        post_url=post_url,
        cancel_url=cancel_url,
        attribute_name=form_attribute_name,
        attribute_value=form_attribute_value,
        attribute_type=form_attribute_type,
        attribute_scope=form_attribute_scope,
    )
예제 #7
0
def entity_index(map_identifier, topic_identifier, entity_identifier,
                 entity_type):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)
    if "admin" not in current_user.roles:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    entity = topic_store.get_association(
        map_identifier,
        entity_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if entity is None:
        entity = topic_store.get_occurrence(map_identifier, entity_identifier)

    if entity is None:
        abort(404)

    return_url = None
    if entity_type == "association":
        return_url = "association.index"
    elif entity_type == "image":
        return_url = "image.index"
    elif entity_type == "3d-scene":
        return_url = "three_d.index"
    elif entity_type == "file":
        return_url = "file.index"
    elif entity_type == "link":
        return_url = "link.index"
    elif entity_type == "video":
        return_url = "video.index"

    attributes = []
    entity_attributes = topic_store.get_attributes(map_identifier,
                                                   entity_identifier)

    for entity_attribute in entity_attributes:
        attributes.append({
            "identifier": entity_attribute.identifier,
            "name": entity_attribute.name,
            "value": entity_attribute.value,
            "type": str(entity_attribute.data_type).lower(),
            "scope": entity_attribute.scope,
        })

    creation_date_attribute = topic.get_attribute_by_name("creation-timestamp")
    creation_date = (maya.parse(creation_date_attribute.value)
                     if creation_date_attribute else "Undefined")

    return render_template(
        "attribute/index.html",
        topic_map=topic_map,
        topic=topic,
        entity=entity,
        entity_type=entity_type,
        return_url=return_url,
        attributes=attributes,
        creation_date=creation_date,
    )
예제 #8
0
def delete(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]

    if request.method == "POST":
        try:
            # Remove the topic from the topic store
            topic_store.delete_topic(map_identifier, topic_identifier)

            # Clear the breadcrumbs (of which this topic was part of)
            session["breadcrumbs"] = []

            # Remove the topic's resources directory
            topic_directory = os.path.join(bp.root_path, RESOURCES_DIRECTORY,
                                           str(map_identifier),
                                           topic_identifier)
            if os.path.isdir(topic_directory):
                shutil.rmtree(topic_directory)
        except TopicDbError:
            flash(
                "Topic not deleted. Certain predefined topics cannot be deleted. Alternatively, you attempted to delete an association.",
                "warning",
            )
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic_identifier,
                ))

        flash("Topic successfully deleted.", "success")
        return redirect(
            url_for(
                "topic.view",
                map_identifier=topic_map.identifier,
                topic_identifier="home",
            ))

    return render_template(
        "topic/delete.html",
        topic_map=topic_map,
        topic=topic,
        map_notes_count=map_notes_count,
    )
예제 #9
0
def add_note(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and (
            topic_map.collaboration_mode is not CollaborationMode.EDIT
            and topic_map.collaboration_mode is not CollaborationMode.COMMENT):
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]
    error = 0

    if request.method == "POST":
        form_note_title = request.form["note-title"].strip()
        form_note_text = request.form["note-text"].strip()
        form_note_scope = request.form["note-scope"].strip()

        # If no values have been provided set their default values
        if not form_note_scope:
            form_note_scope = session["current_scope"]

        # Validate form inputs
        if not form_note_title:
            error = error | 1
        if not form_note_text:
            error = error | 2
        if not topic_store.topic_exists(topic_map.identifier, form_note_scope):
            error = error | 4

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            note_occurrence = Occurrence(
                instance_of="note",
                topic_identifier=topic.identifier,
                scope=form_note_scope,
                resource_data=form_note_text,
            )
            title_attribute = Attribute(
                "title",
                form_note_title,
                note_occurrence.identifier,
                data_type=DataType.STRING,
            )
            timestamp = str(datetime.now())
            modification_attribute = Attribute(
                "modification-timestamp",
                timestamp,
                note_occurrence.identifier,
                data_type=DataType.TIMESTAMP,
            )

            # Persist objects to the topic store
            topic_store.create_occurrence(topic_map.identifier,
                                          note_occurrence)
            topic_store.create_attribute(topic_map.identifier, title_attribute)
            topic_store.create_attribute(topic_map.identifier,
                                         modification_attribute)

            flash("Note successfully added.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

        return render_template(
            "topic/add_note.html",
            error=error,
            topic_map=topic_map,
            topic=topic,
            note_title=form_note_title,
            note_text=form_note_text,
            note_scope=form_note_scope,
            map_notes_count=map_notes_count,
        )

    return render_template(
        "topic/add_note.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        map_notes_count=map_notes_count,
    )
예제 #10
0
def view(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    collaboration_mode = None
    is_map_owner = False
    if current_user.is_authenticated:  # User is logged in
        is_map_owner = topic_store.is_map_owner(map_identifier,
                                                current_user.id)
        if is_map_owner:
            topic_map = topic_store.get_map(map_identifier, current_user.id)
        else:
            topic_map = topic_store.get_map(map_identifier)
        if topic_map is None:
            current_app.logger.warning(
                f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
            )
            abort(404)
        collaboration_mode = topic_store.get_collaboration_mode(
            map_identifier, current_user.id)
        if topic_map.published:
            if not is_map_owner and topic_identifier == "home":
                flash(
                    "You are accessing a published topic map of another user.",
                    "primary",
                )
        else:
            if not is_map_owner:  # The map is private and doesn't belong to the user who is trying to access it
                if not collaboration_mode:  # The user is not collaborating on the map
                    abort(403)
    else:  # User is not logged in
        topic_map = topic_store.get_map(map_identifier)
        if topic_map is None:
            current_app.logger.warning(
                f"Topic map not found: user identifier: [N/A], topic map identifier: [{map_identifier}]"
            )
            abort(404)
        if not topic_map.published:  # User is not logged in and the map is not published
            abort(403)

    # Determine if (active) scope filtering has been specified in the URL
    scope_filtered = request.args.get("filter", type=int)
    if scope_filtered is not None:
        session["scope_filter"] = scope_filtered
    if "scope_filter" in session:
        scope_filtered = session["scope_filter"]
    else:
        session["breadcrumbs"] = []
        session["current_scope"] = UNIVERSAL_SCOPE
        session["scope_filter"] = 1

    # If a scope has been specified in the URL, then use that to set the scope
    scope_identifier = request.args.get("scope", type=str)
    if scope_identifier and topic_store.topic_exists(map_identifier,
                                                     scope_identifier):
        session["current_scope"] = scope_identifier

    # Get topic
    if scope_filtered:
        topic = topic_store.get_topic(
            map_identifier,
            topic_identifier,
            scope=session["current_scope"],
            resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
        )
    else:
        topic = topic_store.get_topic(
            map_identifier,
            topic_identifier,
            resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
        )
    if topic is None:
        if current_user.is_authenticated:  # User is logged in
            current_app.logger.warning(
                f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
            )
        else:
            current_app.logger.warning(
                f"Topic not found: user identifier: [N/A], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
            )
        session["inexistent_topic_identifier"] = topic_identifier
        abort(404)
    else:
        session.pop("inexistent_topic_identifier", None)

    if scope_filtered:
        topic_occurrences = topic_store.get_topic_occurrences(
            map_identifier,
            topic_identifier,
            scope=session["current_scope"],
            inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
            resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
        )
    else:
        topic_occurrences = topic_store.get_topic_occurrences(
            map_identifier,
            topic_identifier,
            inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
            resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
        )
    occurrences = {
        "text": None,
        "images": [],
        "3d-scenes": [],
        "files": [],
        "links": [],
        "videos": [],
        "notes": [],
    }
    for occurrence in topic_occurrences:
        if occurrence.instance_of == "text" and occurrence.scope == session[
                "current_scope"]:
            if occurrence.resource_data:
                markdown = mistune.create_markdown(
                    renderer=HighlightRenderer(escape=False),
                    plugins=[
                        "strikethrough",
                        "footnotes",
                        "table",
                    ],
                )
                occurrences["text"] = markdown(
                    occurrence.resource_data.decode())
        elif occurrence.instance_of == "image":
            occurrences["images"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "3d-scene":
            occurrences["3d-scenes"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "file":
            occurrences["files"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "url":
            occurrences["links"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "video":
            occurrences["videos"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "note":
            markdown = mistune.create_markdown(
                renderer=HighlightRenderer(escape=False),
                plugins=[
                    "strikethrough",
                    "footnotes",
                    "table",
                ],
            )
            occurrences["notes"].append({
                "identifier":
                occurrence.identifier,
                "title":
                occurrence.get_attribute_by_name("title").value,
                "timestamp":
                maya.parse(
                    occurrence.get_attribute_by_name(
                        "modification-timestamp").value),
                "text":
                markdown(occurrence.resource_data.decode()),
            })
    if scope_filtered:
        associations = topic_store.get_association_groups(
            map_identifier, topic_identifier, scope=session["current_scope"])
    else:
        associations = topic_store.get_association_groups(
            map_identifier, topic_identifier)

    is_knowledge_path_topic = (("navigation", "up") in associations
                               or ("navigation", "down") in associations
                               or ("navigation", "previous") in associations
                               or ("navigation", "next") in associations)

    creation_date = maya.parse(
        topic.get_attribute_by_name("creation-timestamp").value)
    modification_date_attribute = topic.get_attribute_by_name(
        "modification-timestamp")
    modification_date = maya.parse(
        modification_date_attribute.value
    ) if modification_date_attribute else "Undefined"

    # Breadcrumbs
    if "map_identifier" not in session:
        session["map_identifier"] = topic_map.identifier
    elif session["map_identifier"] != topic_map.identifier:
        session["breadcrumbs"] = []
    session["map_identifier"] = topic_map.identifier

    if "breadcrumbs" not in session:
        session["breadcrumbs"] = []
    breadcrumbs = deque(session["breadcrumbs"], BREADCRUMBS_COUNT)
    if topic_identifier in breadcrumbs:
        breadcrumbs.remove(topic_identifier)
    breadcrumbs.append(topic_identifier)
    session["breadcrumbs"] = list(breadcrumbs)

    from contextualise import api

    associations_state = api.get_association_groups(map_identifier,
                                                    topic_identifier,
                                                    session["current_scope"],
                                                    scope_filtered)
    associations_state = (associations_state[RESPONSE].data.decode("utf-8")
                          if associations_state[STATUS_CODE] == 200 else None)

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]

    return render_template(
        "topic/view.html",
        topic_map=topic_map,
        topic=topic,
        occurrences=occurrences,
        associations=associations,
        associations_state=associations_state,
        is_knowledge_path_topic=is_knowledge_path_topic,
        creation_date=creation_date,
        modification_date=modification_date,
        breadcrumbs=breadcrumbs,
        collaboration_mode=collaboration_mode,
        is_map_owner=is_map_owner,
        map_notes_count=map_notes_count,
    )
예제 #11
0
def edit(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        scope=session["current_scope"],
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    occurrences = topic_store.get_topic_occurrences(
        map_identifier,
        topic_identifier,
        scope=session["current_scope"],
        inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
    )

    texts = [
        occurrence for occurrence in occurrences
        if occurrence.instance_of == "text"
        and occurrence.scope == session["current_scope"]
    ]

    form_topic_name = topic.first_base_name.name
    form_topic_text = texts[0].resource_data.decode(
    ) if len(texts) > 0 and texts[0].resource_data else ""
    form_topic_instance_of = topic.instance_of
    form_topic_text_scope = texts[0].scope if len(
        texts) > 0 else session["current_scope"]

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]
    error = 0

    if request.method == "POST":
        form_topic_name = request.form["topic-name"].strip()
        form_topic_text = request.form["topic-text"].strip()
        form_topic_instance_of = request.form["topic-instance-of"].strip()
        form_topic_text_scope = request.form["topic-text-scope"].strip()

        # If no values have been provided set their default values
        if not form_topic_instance_of:
            form_topic_instance_of = "topic"
        if not form_topic_text_scope:
            form_topic_text_scope = session["current_scope"]

        # Validate form inputs
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_instance_of):
            error = error | 1
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_text_scope):
            error = error | 2

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            if topic.get_base_name_by_scope(session["current_scope"]):
                # Update the topic's base name if it has changed
                if topic.first_base_name.name != form_topic_name:
                    topic_store.update_base_name(
                        map_identifier,
                        topic.first_base_name.identifier,
                        form_topic_name,
                        form_topic_text_scope,
                    )
            else:
                base_name = BaseName(form_topic_name, session["current_scope"])
                topic_store.create_base_name(map_identifier, topic.identifier,
                                             base_name)

            # Update topic's 'instance of' if it has changed
            if topic.instance_of != form_topic_instance_of:
                topic_store.update_topic_instance_of(map_identifier,
                                                     topic.identifier,
                                                     form_topic_instance_of)

            # If the topic has an existing text occurrence update it, otherwise create a new text occurrence
            # and persist it
            if len(texts
                   ) > 0 and form_topic_text_scope == session["current_scope"]:
                topic_store.update_occurrence_data(map_identifier,
                                                   texts[0].identifier,
                                                   form_topic_text)
            else:
                text_occurrence = Occurrence(
                    instance_of="text",
                    topic_identifier=topic.identifier,
                    scope=form_topic_text_scope,
                    resource_data=form_topic_text,
                )
                topic_store.create_occurrence(topic_map.identifier,
                                              text_occurrence)

            # Update the topic's modification (timestamp) attribute
            timestamp = str(datetime.now())
            if topic.get_attribute_by_name("modification-timestamp"):
                topic_store.update_attribute_value(
                    topic_map.identifier,
                    topic.get_attribute_by_name(
                        "modification-timestamp").identifier,
                    timestamp,
                )
            else:
                modification_attribute = Attribute(
                    "modification-timestamp",
                    timestamp,
                    topic.identifier,
                    data_type=DataType.TIMESTAMP,
                )
                topic_store.create_attribute(topic_map.identifier,
                                             modification_attribute)

            flash("Topic successfully updated.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "topic/edit.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        topic_name=form_topic_name,
        topic_identifier=topic.identifier,
        topic_text=form_topic_text,
        topic_instance_of=form_topic_instance_of,
        topic_text_scope=form_topic_text_scope,
        collaboration_mode=topic_map.collaboration_mode,
        map_notes_count=map_notes_count,
    )
예제 #12
0
def edit_identifier(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    form_topic_identifier = topic.identifier
    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]
    error = 0

    if request.method == "POST":
        form_topic_identifier = request.form["topic-identifier"].strip()

        # Validate form inputs
        if not form_topic_identifier:
            error = error | 1
        if topic_store.topic_exists(topic_map.identifier,
                                    form_topic_identifier):
            error = error | 2

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            # Update topic identifier
            try:
                topic_store.update_topic_identifier(map_identifier,
                                                    topic_identifier,
                                                    form_topic_identifier)
            except TopicDbError:
                flash(
                    "Topic identifier not updated. Certain predefined topics cannot be modified.",
                    "warning",
                )
                return redirect(
                    url_for(
                        "topic.edit_identifier",
                        map_identifier=topic_map.identifier,
                        topic_identifier=topic_identifier,
                    ))

            flash("Identifier successfully updated.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=form_topic_identifier,
                ))

    return render_template(
        "topic/edit_identifier.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        topic_identifier=form_topic_identifier,
        map_notes_count=map_notes_count,
    )
예제 #13
0
def change_scope(map_identifier, topic_identifier, scope_identifier):
    topic_store = get_topic_store()
    topic_map = topic_store.get_map(map_identifier, current_user.id)

    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    form_scope = scope_identifier
    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]
    error = 0

    if request.method == "POST":
        form_scope = request.form["new-scope"].strip()

        # If no values have been provided set their default values
        if not form_scope:
            form_scope = UNIVERSAL_SCOPE

        # Validate form inputs
        if not topic_store.topic_exists(topic_map.identifier, form_scope):
            error = error | 1

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            session["current_scope"] = form_scope
            flash("Scope successfully changed.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "topic/change_scope.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        scope_identifier=form_scope,
        map_notes_count=map_notes_count,
    )
예제 #14
0
def upload(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]
    error = 0

    if request.method == "POST":
        form_file_title = request.form["file-title"].strip()
        form_file_scope = request.form["file-scope"].strip()
        form_upload_file = request.files[
            "file-file"] if "file-file" in request.files else None

        # If no values have been provided set their default values
        if not form_file_scope:
            form_file_scope = session["current_scope"]

        # Validate form inputs
        if not form_file_title:
            error = error | 1
        if not form_upload_file:
            error = error | 2
        else:
            if form_upload_file.filename == "":
                error = error | 4
        if not topic_store.topic_exists(topic_map.identifier, form_file_scope):
            error = error | 8

        if error != 0:
            flash(
                "An error occurred when uploading the 3D content. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            file_extension = get_file_extension(form_upload_file.filename)
            file_file_name = f"{str(uuid.uuid4())}.{file_extension}"

            # Create the file directory for this topic map if it doesn't already exist
            file_directory = os.path.join(current_app.static_folder,
                                          RESOURCES_DIRECTORY,
                                          str(map_identifier))
            if not os.path.isdir(file_directory):
                os.makedirs(file_directory)

            file_path = os.path.join(file_directory, file_file_name)
            form_upload_file.save(file_path)

            file_occurrence = Occurrence(
                instance_of="3d-scene",
                topic_identifier=topic.identifier,
                scope=form_file_scope,
                resource_ref=file_file_name,
            )
            title_attribute = Attribute(
                "title",
                form_file_title,
                file_occurrence.identifier,
                data_type=DataType.STRING,
            )

            # Persist objects to the topic store
            topic_store.create_occurrence(topic_map.identifier,
                                          file_occurrence)
            topic_store.create_attribute(topic_map.identifier, title_attribute)

            flash("3D content successfully uploaded.", "success")
            return redirect(
                url_for(
                    "three_d.index",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

        return render_template(
            "three_d/upload.html",
            error=error,
            topic_map=topic_map,
            topic=topic,
            file_title=form_file_title,
            file_scope=form_file_scope,
            map_notes_count=map_notes_count,
        )

    return render_template(
        "three_d/upload.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        map_notes_count=map_notes_count,
    )
예제 #15
0
def edit_note(map_identifier, topic_identifier, note_identifier):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    if topic is None:
        abort(404)

    note_occurrence = topic_store.get_occurrence(
        map_identifier,
        note_identifier,
        inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    form_note_title = note_occurrence.get_attribute_by_name("title").value
    form_note_text = note_occurrence.resource_data.decode()
    form_note_scope = note_occurrence.scope

    error = 0

    if request.method == "POST":
        form_note_title = request.form["note-title"].strip()
        form_note_text = request.form["note-text"].strip()
        form_note_scope = request.form["note-scope"].strip()

        # If no values have been provided set their default values
        if not form_note_scope:
            form_note_scope = "*"  # Universal scope

        # Validate form inputs
        if not form_note_title:
            error = error | 1
        if not form_note_text:
            error = error | 2
        if not topic_store.topic_exists(topic_map.identifier, form_note_scope):
            error = error | 4

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            # Update note's title if it has changed
            if note_occurrence.get_attribute_by_name(
                    "title").value != form_note_title:
                topic_store.update_attribute_value(
                    topic_map.identifier,
                    note_occurrence.get_attribute_by_name("title").identifier,
                    form_note_title,
                )

            # Update the note's modification (timestamp) attribute
            timestamp = str(datetime.now())
            topic_store.update_attribute_value(
                topic_map.identifier,
                note_occurrence.get_attribute_by_name(
                    "modification-timestamp").identifier,
                timestamp,
            )

            # Update note (occurrence)
            topic_store.update_occurrence_data(map_identifier,
                                               note_occurrence.identifier,
                                               form_note_text)

            # Update note's scope if it has changed
            if note_occurrence.scope != form_note_scope:
                topic_store.update_occurrence_scope(map_identifier,
                                                    note_occurrence.identifier,
                                                    form_note_scope)

            flash("Note successfully updated.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "topic/edit_note.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        note_identifier=note_occurrence.identifier,
        note_title=form_note_title,
        note_text=form_note_text,
        note_scope=form_note_scope,
    )
예제 #16
0
def edit_note(map_identifier, topic_identifier, note_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and (
            topic_map.collaboration_mode is not CollaborationMode.EDIT
            and topic_map.collaboration_mode is not CollaborationMode.COMMENT):
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    note_occurrence = topic_store.get_occurrence(
        map_identifier,
        note_identifier,
        inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    form_note_title = note_occurrence.get_attribute_by_name("title").value
    form_note_text = note_occurrence.resource_data.decode()
    form_note_scope = note_occurrence.scope

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]
    error = 0

    if request.method == "POST":
        form_note_title = request.form["note-title"].strip()
        form_note_text = request.form["note-text"].strip()
        form_note_scope = request.form["note-scope"].strip()

        # If no values have been provided set their default values
        if not form_note_scope:
            form_note_scope = session["current_scope"]

        # Validate form inputs
        if not form_note_title:
            error = error | 1
        if not form_note_text:
            error = error | 2
        if not topic_store.topic_exists(topic_map.identifier, form_note_scope):
            error = error | 4

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            # Update note's title if it has changed
            if note_occurrence.get_attribute_by_name(
                    "title").value != form_note_title:
                topic_store.update_attribute_value(
                    topic_map.identifier,
                    note_occurrence.get_attribute_by_name("title").identifier,
                    form_note_title,
                )

            # Update the note's modification (timestamp) attribute
            timestamp = str(datetime.now())
            topic_store.update_attribute_value(
                topic_map.identifier,
                note_occurrence.get_attribute_by_name(
                    "modification-timestamp").identifier,
                timestamp,
            )

            # Update note (occurrence)
            topic_store.update_occurrence_data(map_identifier,
                                               note_occurrence.identifier,
                                               form_note_text)

            # Update note's scope if it has changed
            if note_occurrence.scope != form_note_scope:
                topic_store.update_occurrence_scope(map_identifier,
                                                    note_occurrence.identifier,
                                                    form_note_scope)

            flash("Note successfully updated.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "topic/edit_note.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        note_identifier=note_occurrence.identifier,
        note_title=form_note_title,
        note_text=form_note_text,
        note_scope=form_note_scope,
        map_notes_count=map_notes_count,
    )
예제 #17
0
def entity_add(map_identifier, topic_identifier, entity_identifier,
               entity_type):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)
    if "admin" not in current_user.roles:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    entity = topic_store.get_association(
        map_identifier,
        entity_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if entity is None:
        entity = topic_store.get_occurrence(map_identifier, entity_identifier)

    if entity is None:
        abort(404)

    form_attribute_name = ""
    form_attribute_value = ""
    form_attribute_type = ""
    form_attribute_scope = "*"

    error = 0

    if request.method == "POST":
        form_attribute_name = request.form["attribute-name"].strip()
        form_attribute_value = request.form["attribute-value"].strip()
        form_attribute_type = request.form["attribute-type"]
        form_attribute_scope = request.form["attribute-scope"].strip()

        # If no values have been provided set their default values
        if not form_attribute_scope:
            form_attribute_scope = "*"  # Universal scope

        # Validate form inputs
        if not form_attribute_name:
            error = error | 1
        if not form_attribute_value:
            error = error | 2
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_attribute_scope):
            error = error | 4

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            attribute = Attribute(
                form_attribute_name,
                form_attribute_value,
                entity.identifier,
                data_type=DataType[form_attribute_type],
            )

            # Persist objects to the topic store
            topic_store.set_attribute(topic_map.identifier, attribute)

            flash("Attribute successfully added.", "success")
            return redirect(
                url_for(
                    "attribute.entity_index",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                    entity_identifier=entity.identifier,
                    entity_type=entity_type,
                ))

    post_url = "attribute.entity_add"
    cancel_url = "attribute.entity_index"

    return render_template(
        "attribute/add.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        entity=entity,
        entity_type=entity_type,
        post_url=post_url,
        cancel_url=cancel_url,
        attribute_name=form_attribute_name,
        attribute_value=form_attribute_value,
        attribute_type=form_attribute_type,
        attribute_scope=form_attribute_scope,
    )
예제 #18
0
def delete_note(map_identifier, topic_identifier, note_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and (
            topic_map.collaboration_mode is not CollaborationMode.EDIT
            and topic_map.collaboration_mode is not CollaborationMode.COMMENT):
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    note_occurrence = topic_store.get_occurrence(
        map_identifier,
        note_identifier,
        inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    form_note_title = note_occurrence.get_attribute_by_name("title").value
    markdown = mistune.create_markdown(
        renderer=HighlightRenderer(escape=False),
        plugins=[
            "strikethrough",
            "footnotes",
            "table",
        ],
    )
    form_note_text = markdown(note_occurrence.resource_data.decode())
    form_note_scope = note_occurrence.scope

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]

    if request.method == "POST":
        topic_store.delete_occurrence(map_identifier,
                                      note_occurrence.identifier)
        flash("Note successfully deleted.", "warning")
        return redirect(
            url_for(
                "topic.view",
                map_identifier=topic_map.identifier,
                topic_identifier=topic.identifier,
            ))

    return render_template(
        "topic/delete_note.html",
        topic_map=topic_map,
        topic=topic,
        note_identifier=note_occurrence.identifier,
        note_title=form_note_title,
        note_text=form_note_text,
        note_scope=form_note_scope,
        map_notes_count=map_notes_count,
    )
예제 #19
0
def entity_delete(
    map_identifier,
    topic_identifier,
    entity_identifier,
    attribute_identifier,
    entity_type,
):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)
    if "admin" not in current_user.roles:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    entity = topic_store.get_association(
        map_identifier,
        entity_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if entity is None:
        entity = topic_store.get_occurrence(map_identifier, entity_identifier)

    if entity is None:
        abort(404)

    attribute = topic_store.get_attribute(map_identifier, attribute_identifier)
    if attribute is None:
        abort(404)

    form_attribute_name = attribute.name
    form_attribute_value = attribute.value
    form_attribute_type = str(attribute.data_type).capitalize()
    form_attribute_scope = attribute.scope

    if request.method == "POST":
        # Delete attribute from topic store
        topic_store.delete_attribute(map_identifier, attribute.identifier)

        flash("Attribute successfully deleted.", "warning")
        return redirect(
            url_for(
                "attribute.entity_index",
                map_identifier=topic_map.identifier,
                topic_identifier=topic.identifier,
                entity_identifier=entity.identifier,
                entity_type=entity_type,
            ))

    post_url = "attribute.entity_delete"
    cancel_url = "attribute.entity_index"

    return render_template(
        "attribute/delete.html",
        topic_map=topic_map,
        topic=topic,
        entity=entity,
        attribute=attribute,
        entity_type=entity_type,
        post_url=post_url,
        cancel_url=cancel_url,
        attribute_name=form_attribute_name,
        attribute_value=form_attribute_value,
        attribute_type=form_attribute_type,
        attribute_scope=form_attribute_scope,
    )
예제 #20
0
def add_name(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]
    error = 0

    if request.method == "POST":
        form_topic_name = request.form["topic-name"].strip()
        form_topic_name_scope = request.form["topic-name-scope"].strip()

        # If no values have been provided set their default values
        if not form_topic_name_scope:
            form_topic_name_scope = session["current_scope"]

        # Validate form inputs
        if not form_topic_name:
            error = error | 1
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_name_scope):
            error = error | 2

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            base_name = BaseName(form_topic_name, scope=form_topic_name_scope)
            topic_store.create_base_name(map_identifier, topic.identifier,
                                         base_name)

            flash("Name successfully added.", "success")
            return redirect(
                url_for(
                    "topic.view_names",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

        return render_template(
            "topic/add_name.html",
            error=error,
            topic_map=topic_map,
            topic=topic,
            topic_name=form_topic_name,
            topic_name_scope=form_topic_name_scope,
            map_notes_count=map_notes_count,
        )

    return render_template(
        "topic/add_name.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        map_notes_count=map_notes_count,
    )
예제 #21
0
def add_reference(map_identifier, topic_identifier, association_identifier,
                  member_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_topic_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    association = topic_store.get_association(map_identifier,
                                              association_identifier)
    if association is None:
        abort(404)

    member = association.get_member(member_identifier)

    error = 0

    if request.method == "POST":
        form_topic_reference = request.form["topic-reference"].strip()

        # Validate form inputs
        if not form_topic_reference:
            error = error | 1
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_reference):
            error = error | 2

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            member.add_topic_ref(form_topic_reference)
            topic_store.delete_association(map_identifier,
                                           association_identifier)
            topic_store.set_association(map_identifier, association)

            flash("Topic reference successfully added.", "success")
            return redirect(
                url_for(
                    "association.view_member",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                    association_identifier=association_identifier,
                    member_identifier=member_identifier,
                ))

        return render_template(
            "association/add_reference.html",
            error=error,
            topic_map=topic_map,
            topic=topic,
            association=association,
            member=member,
            topic_reference=form_topic_reference,
        )

    return render_template(
        "association/add_reference.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        association=association,
        member=member,
    )
예제 #22
0
def create(map_identifier, topic_identifier):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    form_topic_name = ""
    form_topic_identifier = ""
    form_topic_text = ""
    form_topic_instance_of = "topic"
    form_topic_text_scope = session["current_scope"]

    error = 0

    if request.method == "POST":
        form_topic_identifier = request.form["topic-identifier"].strip()
        form_topic_name = request.form["topic-name"].strip()
        form_topic_text = request.form["topic-text"].strip()
        form_topic_instance_of = request.form["topic-instance-of"].strip()
        form_topic_text_scope = request.form["topic-text-scope"].strip()

        # If no values have been provided set their default values
        if not form_topic_instance_of:
            form_topic_instance_of = "topic"

        # Validate form inputs
        if not form_topic_name:
            error = error | 1
        if topic_store.topic_exists(topic_map.identifier,
                                    form_topic_identifier):
            error = error | 2
        if not form_topic_identifier:
            error = error | 4
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_instance_of):
            error = error | 8
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_text_scope):
            error = error | 16

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            new_topic = Topic(form_topic_identifier, form_topic_instance_of,
                              form_topic_name)
            text_occurrence = Occurrence(
                instance_of="text",
                topic_identifier=new_topic.identifier,
                scope=form_topic_text_scope,
                resource_data=form_topic_text,
            )
            timestamp = str(datetime.now())
            modification_attribute = Attribute(
                "modification-timestamp",
                timestamp,
                new_topic.identifier,
                data_type=DataType.TIMESTAMP,
            )

            query_attribute = Attribute(
                "knowledge-graph-query",
                form_topic_name.lower(),
                new_topic.identifier,
                data_type=DataType.STRING,
            )

            # Persist objects to the topic store
            topic_store.set_topic(topic_map.identifier, new_topic)
            topic_store.set_occurrence(topic_map.identifier, text_occurrence)
            topic_store.set_attribute(topic_map.identifier,
                                      modification_attribute)
            topic_store.set_attribute(topic_map.identifier, query_attribute)

            flash("Topic successfully created.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=new_topic.identifier,
                ))

    return render_template(
        "topic/create.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        topic_name=form_topic_name,
        topic_identifier=form_topic_identifier,
        topic_text=form_topic_text,
        topic_instance_of=form_topic_instance_of,
        topic_text_scope=form_topic_text_scope,
    )
예제 #23
0
def create(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_topic_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    error = 0

    flash(
        "Take a look at this page to see practical examples of <a href='https://brettkromkamp.com/posts/semantically-meaningful-relationships/'>semantically meaningful relationships</a>.",
        "info",
    )

    if request.method == "POST":
        form_association_dest_topic_ref = request.form[
            "association-dest-topic-ref"].strip()
        form_association_dest_role_spec = request.form[
            "association-dest-role-spec"].strip()
        form_association_src_topic_ref = topic_identifier
        form_association_src_role_spec = request.form[
            "association-src-role-spec"].strip()
        form_association_instance_of = request.form[
            "association-instance-of"].strip()
        form_association_scope = request.form["association-scope"].strip()
        form_association_name = request.form["association-name"].strip()
        form_association_identifier = request.form[
            "association-identifier"].strip()

        # If no values have been provided set their default values
        if not form_association_dest_role_spec:
            form_association_dest_role_spec = "related"
        if not form_association_src_role_spec:
            form_association_src_role_spec = "related"
        if not form_association_instance_of:
            form_association_instance_of = "association"
        if not form_association_scope:
            form_association_scope = session["current_scope"]
        if not form_association_name:
            form_association_name = "Undefined"
        if not form_association_identifier:
            form_association_identifier = ""

        # Validate form inputs
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_association_dest_topic_ref):
            error = error | 1
        if form_association_dest_role_spec != "related" and not topic_store.topic_exists(
                topic_map.identifier, form_association_dest_role_spec):
            error = error | 2
        if form_association_src_role_spec != "related" and not topic_store.topic_exists(
                topic_map.identifier, form_association_src_role_spec):
            error = error | 4
        if form_association_instance_of != "association" and not topic_store.topic_exists(
                topic_map.identifier, form_association_instance_of):
            error = error | 8
        if form_association_scope != UNIVERSAL_SCOPE and not topic_store.topic_exists(
                topic_map.identifier, form_association_scope):
            error = error | 16
        if form_association_identifier and topic_store.topic_exists(
                topic_map.identifier, form_association_identifier):
            error = error | 32

        # TODO: Flag an error to prevent the user from creating an association with the reserved
        # 'navigation' or 'categorization' types

        # If role identifier topics are missing then create them
        if error & 2:  # Destination role spec
            pass
        if error & 4:  # Source role spec
            pass

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            association = Association(
                identifier=form_association_identifier,
                instance_of=form_association_instance_of,
                name=form_association_name,
                scope=form_association_scope,
                src_topic_ref=form_association_src_topic_ref,
                dest_topic_ref=form_association_dest_topic_ref,
                src_role_spec=form_association_src_role_spec,
                dest_role_spec=form_association_dest_role_spec,
            )

            # Persist association object to the topic store
            topic_store.set_association(map_identifier, association)

            flash("Association successfully created.", "success")
            return redirect(
                url_for(
                    "association.index",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic_identifier,
                ))

        return render_template(
            "association/create.html",
            error=error,
            topic_map=topic_map,
            topic=topic,
            association_instance_of=form_association_instance_of,
            association_src_topic_ref=form_association_src_topic_ref,
            association_src_role_spec=form_association_src_role_spec,
            association_dest_topic_ref=form_association_dest_topic_ref,
            association_dest_role_spec=form_association_dest_role_spec,
            association_scope=form_association_scope,
            association_name=form_association_name,
            association_identifier=form_association_identifier,
        )

    return render_template(
        "association/create.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
    )
예제 #24
0
def view(map_identifier, topic_identifier):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)
    else:
        if topic_map.shared:
            if current_user.is_authenticated:  # User is logged in
                if (current_user.id != topic_map.user_identifier
                        and topic_identifier == "home"):
                    flash(
                        "You are accessing a shared topic map of another user.",
                        "primary",
                    )
        else:
            if current_user.is_authenticated:  # User is logged in
                if current_user.id != topic_map.user_identifier:
                    abort(403)
            else:  # User is *not* logged in
                abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        session["inexistent_topic_identifier"] = topic_identifier
        abort(404)
    else:
        session.pop("inexistent_topic_identifier", None)

    # Determine if (active) scope filtering has been specified in the URL
    scope_filtered = request.args.get("filter", type=int)
    if scope_filtered is not None:
        session["scope_filter"] = scope_filtered
    scope_filtered = session["scope_filter"]

    # If a context has been specified in the URL, then use that to set the context
    scope_identifier = request.args.get("context", type=str)
    if scope_identifier and topic_store.topic_exists(map_identifier,
                                                     scope_identifier):
        session["current_scope"] = scope_identifier

    if scope_filtered:
        topic_occurrences = topic_store.get_topic_occurrences(
            map_identifier,
            topic_identifier,
            scope=session["current_scope"],
            inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
            resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
        )
    else:
        topic_occurrences = topic_store.get_topic_occurrences(
            map_identifier,
            topic_identifier,
            inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
            resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
        )
    occurrences = {
        "text": None,
        "images": [],
        "3d-scenes": [],
        "files": [],
        "links": [],
        "videos": [],
        "notes": [],
    }
    for occurrence in topic_occurrences:
        if occurrence.instance_of == "text":
            if occurrence.resource_data:
                occurrences["text"] = mistune.markdown(
                    occurrence.resource_data.decode())
        elif occurrence.instance_of == "image":
            occurrences["images"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "3d-scene":
            occurrences["3d-scenes"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "file":
            occurrences["files"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "url":
            occurrences["links"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "video":
            occurrences["videos"].append({
                "title":
                occurrence.get_attribute_by_name("title").value,
                "url":
                occurrence.resource_ref,
            })
        elif occurrence.instance_of == "note":
            occurrences["notes"].append({
                "identifier":
                occurrence.identifier,
                "title":
                occurrence.get_attribute_by_name("title").value,
                "timestamp":
                maya.parse(
                    occurrence.get_attribute_by_name(
                        "modification-timestamp").value),
                "text":
                mistune.markdown(occurrence.resource_data.decode()),
            })
    if scope_filtered:
        associations = topic_store.get_association_groups(
            map_identifier, topic_identifier, scope=session["current_scope"])
    else:
        associations = topic_store.get_association_groups(
            map_identifier, topic_identifier)

    creation_date = maya.parse(
        topic.get_attribute_by_name("creation-timestamp").value)
    modification_date_attribute = topic.get_attribute_by_name(
        "modification-timestamp")
    modification_date = (maya.parse(modification_date_attribute.value)
                         if modification_date_attribute else "Undefined")

    # Breadcrumbs
    if "breadcrumbs" not in session:
        session["breadcrumbs"] = []
    breadcrumbs = deque(session["breadcrumbs"], BREADCRUMBS_COUNT)
    if topic_identifier in breadcrumbs:
        breadcrumbs.remove(topic_identifier)
    breadcrumbs.append(topic_identifier)
    session["breadcrumbs"] = list(breadcrumbs)

    knowledge_graph_query = (
        topic.get_attribute_by_name("knowledge-graph-query").value
        if topic.get_attribute_by_name("knowledge-graph-query") else None)

    return render_template("topic/view.html",
                           topic_map=topic_map,
                           topic=topic,
                           occurrences=occurrences,
                           associations=associations,
                           creation_date=creation_date,
                           modification_date=modification_date,
                           breadcrumbs=breadcrumbs,
                           knowledge_graph_query=knowledge_graph_query)
예제 #25
0
def delete(map_identifier, topic_identifier, image_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_topic_map(map_identifier, current_user.id)
    if topic_map is None:
        abort(404)
    # If the map doesn't belong to the user and they don't have the right collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    image_occurrence = topic_store.get_occurrence(
        map_identifier,
        image_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )

    form_image_title = image_occurrence.get_attribute_by_name("title").value
    form_image_resource_ref = image_occurrence.resource_ref
    form_image_scope = image_occurrence.scope

    if request.method == "POST":
        # Delete image occurrence from topic store
        topic_store.delete_occurrence(map_identifier,
                                      image_occurrence.identifier)

        # Delete image from file system
        image_file_path = os.path.join(
            bp.root_path,
            RESOURCES_DIRECTORY,
            str(map_identifier),
            topic_identifier,
            image_occurrence.resource_ref,
        )
        if os.path.exists(image_file_path):
            os.remove(image_file_path)

        flash("Image successfully deleted.", "warning")
        return redirect(
            url_for(
                "image.index",
                map_identifier=topic_map.identifier,
                topic_identifier=topic.identifier,
            ))

    return render_template(
        "image/delete.html",
        topic_map=topic_map,
        topic=topic,
        image_identifier=image_occurrence.identifier,
        image_title=form_image_title,
        image_resource_ref=form_image_resource_ref,
        image_scope=form_image_scope,
    )
예제 #26
0
def edit(map_identifier, topic_identifier):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    occurrences = topic_store.get_topic_occurrences(
        map_identifier,
        topic_identifier,
        scope=session["current_scope"],
        inline_resource_data=RetrievalMode.INLINE_RESOURCE_DATA,
    )

    texts = [
        occurrence for occurrence in occurrences
        if occurrence.instance_of == "text"
    ]

    form_topic_name = topic.first_base_name.name
    form_topic_text = (texts[0].resource_data.decode()
                       if len(texts) > 0 and texts[0].resource_data else "")
    form_topic_instance_of = topic.instance_of
    form_topic_text_scope = (
        texts[0].scope if len(texts) > 0 else session["current_scope"]
    )  # Should it be '*'?

    error = 0

    if request.method == "POST":
        form_topic_name = request.form["topic-name"].strip()
        form_topic_text = request.form["topic-text"].strip()
        form_topic_instance_of = request.form["topic-instance-of"].strip()
        form_topic_text_scope = request.form["topic-text-scope"].strip()

        # If no values have been provided set their default values
        if not form_topic_instance_of:
            form_topic_instance_of = "topic"

        # Validate form inputs
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_instance_of):
            error = error | 1
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_text_scope):
            error = error | 2

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            # Update topic's first base name if it has changed
            if topic.first_base_name.name != form_topic_name:
                topic_store.update_basename(map_identifier,
                                            topic.first_base_name.identifier,
                                            form_topic_name)

            # Update topic's 'instance of' if it has changed
            if topic.instance_of != form_topic_instance_of:
                topic_store.update_topic_instance_of(map_identifier,
                                                     topic.identifier,
                                                     form_topic_instance_of)

            # If the topic has an existing text occurrence update it, otherwise create a new text occurrence
            # and persist it
            if len(texts) > 0:
                topic_store.update_occurrence_data(map_identifier,
                                                   texts[0].identifier,
                                                   form_topic_text)
            else:
                text_occurrence = Occurrence(
                    instance_of="text",
                    topic_identifier=topic.identifier,
                    scope=form_topic_text_scope,
                    resource_data=form_topic_text,
                )
                topic_store.set_occurrence(topic_map.identifier,
                                           text_occurrence)

            # Update the topic's modification (timestamp) attribute
            timestamp = str(datetime.now())
            if topic.get_attribute_by_name("modification-timestamp"):
                topic_store.update_attribute_value(
                    topic_map.identifier,
                    topic.get_attribute_by_name(
                        "modification-timestamp").identifier,
                    timestamp,
                )
            else:
                modification_attribute = Attribute(
                    "modification-timestamp",
                    timestamp,
                    topic.identifier,
                    data_type=DataType.TIMESTAMP,
                )
                topic_store.set_attribute(topic_map.identifier,
                                          modification_attribute)

            flash("Topic successfully updated.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "topic/edit.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        topic_name=form_topic_name,
        topic_identifier=topic.identifier,
        topic_text=form_topic_text,
        topic_instance_of=form_topic_instance_of,
        topic_text_scope=form_topic_text_scope,
    )
예제 #27
0
def add(map_identifier, topic_identifier):
    topic_store = get_topic_store()

    topic_map = topic_store.get_map(map_identifier, current_user.id)
    if topic_map is None:
        current_app.logger.warning(
            f"Topic map not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}]"
        )
        abort(404)
    # If the map doesn't belong to the user and they don't have the right
    # collaboration mode on the map, then abort
    if not topic_map.owner and topic_map.collaboration_mode is not CollaborationMode.EDIT:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        current_app.logger.warning(
            f"Topic not found: user identifier: [{current_user.id}], topic map identifier: [{map_identifier}], topic identifier: [{topic_identifier}]"
        )
        abort(404)

    form_tags = None

    map_notes_count = topic_store.get_topic_occurrences_statistics(
        map_identifier, "notes")["note"]
    error = 0

    if request.method == "POST":
        form_tags = request.form["topic-tags"].strip()

        # Validate form inputs
        if not form_tags:
            error = error | 1

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            for form_tag in form_tags.split(","):
                topic_store.create_tag(map_identifier, topic.identifier,
                                       form_tag)

            flash("Tags successfully added.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "tag/add.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        topic_name=form_tags,
        map_notes_count=map_notes_count,
    )
예제 #28
0
def add_note(map_identifier, topic_identifier):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    form_note_title = ""
    form_note_text = ""
    form_note_scope = "*"

    error = 0

    if request.method == "POST":
        form_note_title = request.form["note-title"].strip()
        form_note_text = request.form["note-text"].strip()
        form_note_scope = request.form["note-scope"].strip()

        # If no values have been provided set their default values
        if not form_note_scope:
            form_note_scope = "*"  # Universal scope

        # Validate form inputs
        if not form_note_title:
            error = error | 1
        if not form_note_text:
            error = error | 2
        if not topic_store.topic_exists(topic_map.identifier, form_note_scope):
            error = error | 4

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            note_occurrence = Occurrence(
                instance_of="note",
                topic_identifier=topic.identifier,
                scope=form_note_scope,
                resource_data=form_note_text,
            )
            title_attribute = Attribute(
                "title",
                form_note_title,
                note_occurrence.identifier,
                data_type=DataType.STRING,
            )
            timestamp = str(datetime.now())
            modification_attribute = Attribute(
                "modification-timestamp",
                timestamp,
                note_occurrence.identifier,
                data_type=DataType.TIMESTAMP,
            )

            # Persist objects to the topic store
            topic_store.set_occurrence(topic_map.identifier, note_occurrence)
            topic_store.set_attribute(topic_map.identifier, title_attribute)
            topic_store.set_attribute(topic_map.identifier,
                                      modification_attribute)

            flash("Note successfully added.", "success")
            return redirect(
                url_for(
                    "topic.view",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "topic/add_note.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        note_title=form_note_title,
        note_text=form_note_text,
        note_scope=form_note_scope,
    )
예제 #29
0
def create():
    topic_store = get_topic_store()

    form_map_name = ""
    form_map_description = ""
    form_map_shared = False

    error = 0

    if request.method == "POST":
        form_map_name = request.form["map-name"].strip()
        form_map_description = request.form["map-description"].strip()
        form_map_published = True if request.form.get("map-published") == "1" else False
        form_upload_file = request.files["map-image-file"] if "map-image-file" in request.files else None

        # Validate form inputs
        if not form_map_name:
            error = error | 1
        if not form_upload_file:
            error = error | 2
        else:
            if form_upload_file.filename == "":
                error = error | 4
            elif not allowed_file(form_upload_file.filename):
                error = error | 8

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            image_file_name = f"{str(uuid.uuid4())}.{get_file_extension(form_upload_file.filename)}"

            # Create and initialise the topic map
            map_identifier = topic_store.set_topic_map(
                current_user.id,
                form_map_name,
                form_map_description,
                image_file_name,
                initialised=False,
                published=form_map_published,
                promoted=False,
            )
            if map_identifier:
                topic_store.initialise_topic_map(map_identifier, current_user.id)

                # Create the directory for this topic map
                topic_map_directory = os.path.join(bp.root_path, RESOURCES_DIRECTORY, str(map_identifier))
                if not os.path.isdir(topic_map_directory):
                    os.makedirs(topic_map_directory)

                # Upload the image for the topic map to the map's directory
                file_path = os.path.join(topic_map_directory, image_file_name)
                form_upload_file.save(file_path)

                flash("Map successfully created.", "success")
            else:
                flash(
                    "An error occurred while creating the topic map. Get in touch with Support if the problem persists.",
                    "danger",
                )
            return redirect(url_for("map.index"))

    return render_template(
        "map/create.html",
        error=error,
        map_name=form_map_name,
        map_description=form_map_description,
        map_shared=form_map_shared,
    )
예제 #30
0
파일: topic.py 프로젝트: xiba/contextualise
def edit_name(map_identifier, topic_identifier, name_identifier):
    topic_store = get_topic_store()
    topic_map = topic_store.get_topic_map(map_identifier)

    if topic_map is None:
        abort(404)

    if current_user.id != topic_map.user_identifier:
        abort(403)
    if "admin" not in current_user.roles:
        abort(403)

    topic = topic_store.get_topic(
        map_identifier,
        topic_identifier,
        resolve_attributes=RetrievalMode.RESOLVE_ATTRIBUTES,
    )
    if topic is None:
        abort(404)

    form_topic_name = topic.get_base_name(name_identifier).name
    form_topic_name_scope = topic.get_base_name(name_identifier).scope

    error = 0

    if request.method == "POST":
        form_topic_name = request.form["topic-name"].strip()
        form_topic_name_scope = request.form["topic-name-scope"].strip()

        # Validate form inputs
        if not form_topic_name:
            error = error | 1
        if not topic_store.topic_exists(topic_map.identifier,
                                        form_topic_name_scope):
            error = error | 2

        if error != 0:
            flash(
                "An error occurred when submitting the form. Please review the warnings and fix accordingly.",
                "warning",
            )
        else:
            # Update name if required
            if form_topic_name != topic.get_base_name(
                    name_identifier
            ).name or form_topic_name_scope != topic.get_base_name(
                    name_identifier).scope:
                topic_store.update_basename(map_identifier,
                                            name_identifier,
                                            form_topic_name,
                                            scope=form_topic_name_scope)

            flash("Name successfully updated.", "success")
            return redirect(
                url_for(
                    "topic.view_names",
                    map_identifier=topic_map.identifier,
                    topic_identifier=topic.identifier,
                ))

    return render_template(
        "topic/edit_name.html",
        error=error,
        topic_map=topic_map,
        topic=topic,
        topic_name=form_topic_name,
        topic_name_scope=form_topic_name_scope,
        name_identifier=name_identifier,
    )