Skip to content

Commit

Permalink
take new use case into account mviewer#161 (comment)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gaetanbrl committed Apr 14, 2023
1 parent 78ce4e9 commit ab9ed9e
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 51 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ <h4 class="text-primary" i18n="tabs.publication.subfinish">Vous avez terminé la
<div id="cardsPublication" class="my-5">
<!--Publish-->
<div style="max-width: 18rem;" id="onlineCard">
<a role="button" onclick="mv.publish(config?.id)" href="#">
<a role="button" onclick="mv.showNamePublishModal(config?.id)" href="#">
<div class="card border-info p-3 zoomCard h-100" style="max-width: 18rem; background-color: #f5ae54; color: white;">
<div class="card-body mb-5">
<span class="iconCard-finish">
Expand Down
2 changes: 1 addition & 1 deletion js/mviewerstudio.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@ var newConfiguration = function (infos) {
id: infos?.id || mv.uuid(),
description: newDate.format("DD-MM-YYYY-HH-mm-ss"),
isFile: !!infos?.id,
publish: infos?.publish == 'true',
relation: infos?.relation
};
//Store des parametres non gérés
Expand Down Expand Up @@ -915,6 +914,7 @@ var saveApplicationParameters = () => {
config.isFile = true;
document.querySelector("#toolsbarStudio-delete").classList.remove("d-none");
document.querySelector("#layerOptionBtn").classList.remove("d-none");
mv.manageDraftBadge(config.relation);
} else {
config.isFile = false;
}
Expand Down
81 changes: 66 additions & 15 deletions lib/mv.js
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@ var mv = (function () {
var publisher = "anonymous";
var organisation = _userInfo?.groupFullName || "";
var description = document.querySelector("#createVersionInput")?.value || data.description
let relation = data?.relation || "";
const UUID = data.id;
const keyworkds = document.querySelector("#optKeywords")?.value;

Expand Down Expand Up @@ -1217,13 +1218,7 @@ var mv = (function () {
});
},

parseApplication(xml) {
const app_identifier = xml.getElementsByTagName("dc:identifier")[0]?.innerHTML
const app_keywords = xml.getElementsByTagName("dc:keywords")[0]?.innerHTML;
const dateXml = xml.getElementsByTagName("dc:date")[0]?.innerHTML;
const relation = xml.getElementsByTagName("dc:relation")[0]?.innerHTML;
const isPublish = xml.getElementsByTagName("config")[0].getAttribute("publish") == "true";

manageDraftBadge(isPublish) {
if (isPublish) {
document.querySelector("#toolsbarStudio-unpublish").classList.remove("d-none");
document.querySelector(".badge-publish").classList.remove("d-none");
Expand All @@ -1233,11 +1228,20 @@ var mv = (function () {
document.querySelector(".badge-publish").classList.add("d-none");
document.querySelector(".badge-draft").classList.remove("d-none");
}
},

parseApplication(xml) {
const app_identifier = xml.getElementsByTagName("dc:identifier")[0]?.innerHTML
const app_keywords = xml.getElementsByTagName("dc:keywords")[0]?.innerHTML;
const dateXml = xml.getElementsByTagName("dc:date")[0]?.innerHTML;
const relation = xml.getElementsByTagName("dc:relation")[0]?.innerHTML;

mv.manageDraftBadge(relation)
if (_conf.is_php && onlineCard) {
onlineCard.classList.add("d-none")
}

newConfiguration({id: app_identifier, isFile: true, date: dateXml, publish: isPublish, relation: relation});
newConfiguration({id: app_identifier, isFile: true, date: dateXml, relation: relation});
var proxy = $(xml).find("proxy");
var olscompletion = $(xml).find("olscompletion");
if (proxy) {
Expand Down Expand Up @@ -1511,8 +1515,8 @@ var mv = (function () {
</li>`
}];

let badgeLabel = app.publish == "true" ? mviewer.tr("publish") : mviewer.tr("draft");
let badgeColor = app.publish == "true" ? "badge-publish" : "badge-draft";
let badgeLabel = app.relation ? mviewer.tr("publish") : mviewer.tr("draft");
let badgeColor = app.relation ? "badge-publish" : "badge-draft";
let badge = _conf.is_php ? "" : `<span class="badge ${ badgeColor }">${ badgeLabel }</span>`;
const items = `
<div class="list-group-item">
Expand Down Expand Up @@ -1961,6 +1965,49 @@ var mv = (function () {
'data-url':row.xml
}
},
nameNormalizer: (str="") => {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w ]/g, '_').replace(/\s/g, '_').toLowerCase()
},
onChangeName: ({value}, defaultValue) => {
document.querySelector("#sendPublishApp").disabled = conflict && v === defaultName;
},
showNamePublishModal: (id, name = "", conflict = false) => {
if (config.relation) {
return mv.publish(config.id, config.relation)
}
const defaultName = name ? mv.nameNormalizer(name) : mv.nameNormalizer(document.querySelector("#opt-title").value);
const publishAppModal = new bootstrap.Modal('#genericModal');
const question = conflict ? "Ce nom existe déjà ! <br> Vous pouvez changer le nom ou annuler." : "Quel nom souhaitez vous utiliser pour la publication ?"
genericModalContent.innerHTML = "";
genericModalContent.innerHTML = `
<div class="modal-header">
<h5 class="modal-title" i18n="modal.publish.title">Personnalisation</h5>
<button type="button" onclick="" class="close" data-bs-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body">
<p>
<strong>${question}</strong>
</p>
<!-- input-->
<div class="form-group">
<label for="relationPublishName">Nouveau nom :</label>
<input onchange="${conflict} ? : mv.onChangeName(this, ${defaultName}) : null " maxlength="20" minlength="3" value="${defaultName}" type="text" class="form-control" id="relationPublishName">
</div>
<!-- buttons-->
<p><strong>Que souhaitez-vous faire ?</strong></p>
<a id="sendPublishApp" class="cardsClose save-close zoomCard" data-bs-dismiss="modal" onclick="mv.publish('${id}', mv.nameNormalizer(document.getElementById('relationPublishName')?.value))">
<i class="ri-tools-fill"></i>
<span i18n="tabs.publication.publish_title">${mviewer.tr(conflict ? "tabs.publication.publish_retry" : "tabs.publication.publish_title")}</span>
</a>
<a class="cardsClose notsave-close zoomCard" onclick="" data-bs-dismiss="modal">
<i class="ri-home-2-line"></i>
<span i18n="cancel">Annuler</span>
</a>
<a class="returnConf-close" data-bs-target="#genericModal" data-bs-toggle="modal" aria-label="Close"><i class="ri-arrow-left-line"></i> <span i18n="modal.exit.previous">Retour</span></a>
</div>
`;
publishAppModal.show();
},
showPublishModal: (shareLink = "", iframeLink = "", draftLink = "") => {
const publishModal = new bootstrap.Modal('#genericModal');
genericModalContent.innerHTML = "";
Expand Down Expand Up @@ -1999,16 +2046,16 @@ var mv = (function () {
`;
publishModal.show();
},
publish: (id) => {
publish: (id, name = "") => {
if (!id) {
return alertCustom("L'ID n'est pas renseigné. Veuillez contacter un administrateur.", "danger");
}
if (!config.isFile) {
return alertCustom("Enregistrez une premère fois avant de publier !", "danger");
}
fetch(`${ _conf.api }/${id}/publish`)
fetch(`${ _conf.api }/${id}/publish/${name}`,)
.then(r => {
return r.ok ? r.json() : Promise.reject(r)
return r.ok ? r.json() : Promise.reject(r);
})
.then(data => {
if (!_conf?.mviewer_publish) {
Expand All @@ -2026,15 +2073,19 @@ var mv = (function () {
alertCustom("L'application a bien été publiée !", "success");
})
.catch(err => {
alertCustom("Une erreur s'est produite. Veuillez contacter un administrateur.", "danger");
if (err.status == 409) {
mv.showNamePublishModal(id, name, true);
} else {
alertCustom("Une erreur s'est produite. Veuillez contacter un administrateur.", "danger");
}
})
},
refreshOnPublish: (file) => {
const url = _conf.mviewer_instance + file;
loadApplicationParametersFromRemoteFile(url);
},
unpublish: (id) => {
fetch(`${ _conf.api }/${ id }/publish`, { method: "DELETE" })
fetch(`${ _conf.api }/${ id }/publish/${config.relation}`, { method: "DELETE" })
.then(r => {
return r.ok ? r.json() : Promise.reject(r)
})
Expand Down
6 changes: 5 additions & 1 deletion mviewerstudio.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@
"tabs.publication.preview_title" : "Prévisualiser votre application",
"tabs.publication.preview_text" : "Tester et valider votre configuration en live",
"tabs.publication.publish_title" : "Publier votre application",
"tabs.publication.publish_retry" : "Publier avec ce nouveau nom",
"tabs.publication.publish_replace": "Remplacer",
"tabs.publication.publish_text" : "Mettre en ligne votre application et obtenez un lien de partage",
"tabs.data.title": "Thématiques & données",
"tabs.data.themespanel.title": "Panneau des thématiques",
Expand Down Expand Up @@ -530,6 +532,8 @@
"version.comment.ph": "ex: Test release without data",
"studio.toolsbar.unpublish": "Unpublish",
"tabs.publication.publish_title" : "Publish application",
"tabs.publication.publish_text" : "Put your application online."
"tabs.publication.publish_text" : "Put your application online.",
"tabs.publication.publish_retry": "Retry with this new name",
"tabs.publication.publish_replace": "Replace"
}
}
4 changes: 2 additions & 2 deletions srv/python/mviewerstudio_backend/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ConfigModel:
publisher: str
subject: str
date: str
publish: bool
relation: str

def as_dict(self):
return {
Expand All @@ -35,5 +35,5 @@ def as_dict(self):
"url": self.url,
"subject": self.subject,
"date": self.date,
"publish": self.publish
"relation": self.relation
}
58 changes: 41 additions & 17 deletions srv/python/mviewerstudio_backend/route.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from flask import Blueprint, jsonify, Response, request, current_app, redirect
from .utils.login_utils import current_user
from .utils.config_utils import Config
from .utils.config_utils import Config, edit_xml_string, read_xml_file_content
from .utils.commons import clean_preview, init_preview
import hashlib, uuid
from os import path, mkdir, remove
Expand All @@ -11,7 +11,7 @@
from .utils.git_utils import Git_manager
from .utils.register_utils import from_xml_path

from werkzeug.exceptions import BadRequest, MethodNotAllowed
from werkzeug.exceptions import BadRequest, MethodNotAllowed, Conflict

import logging

Expand Down Expand Up @@ -126,44 +126,67 @@ def list_stored_mviewer_config() -> Response:
config["url"] = current_app.config["CONF_PATH_FROM_MVIEWER"] + config["url"]
return jsonify(configs)

@basic_store.route("/api/app/<id>/publish", methods=["GET", "DELETE"])
def publish_mviewer_config(id) -> Response:
@basic_store.route("/api/app/<id>/publish/<name>", methods=["GET", "DELETE"])
def publish_mviewer_config(id, name) -> Response:
"""
Will put online a config.
This route will copy / past XML to publication directory or delete to unpublish.
:param id: configuration UUID
"""
logger.debug("PUBLISH : %s " % id)

xml_publish_name = name

# control publish directory exists
publish_dir = current_app.config["MVIEWERSTUDIO_PUBLISH_PATH"]
if not publish_dir or not path.exists(publish_dir):
return BadRequest("Publish directory does not exists !")
# create or get org parent directory from publication path
org_publish_dir = path.join(current_app.publish_path, current_user.organisation)
if not path.exists(org_publish_dir):
mkdir(org_publish_dir)

# control file to create or replace
past_file = path.join(org_publish_dir, "%s.xml" % xml_publish_name)
if path.exists(past_file) and request.method == "GET":
# read file to replace
content = read_xml_file_content(past_file)
org = content.find(".//metadata/{*}RDF/{*}Description//{*}publisher").text
creator = content.find(".//metadata/{*}RDF/{*}Description//{*}creator").text
identifier = content.find(".//metadata/{*}RDF/{*}Description//{*}identifier").text
lastRelation = content.find(".//metadata/{*}RDF/{*}Description//{*}relation").text
# detect conflict
if lastRelation != xml_publish_name or org != current_user.organisation or creator != current_user.username or identifier != id:
return Conflict("Already exists !")
# replace safely or return bad request
remove(past_file)

# control that workspace to copy exists
workspace = path.join(current_app.config["EXPORT_CONF_FOLDER"], current_user.organisation, id)

if not path.exists(workspace):
return BadRequest("Application does not exists !")

config = current_app.register.read_json(id)

# read config if exists
config = current_app.register.read_json(id)
if not config:
raise BadRequest("This config doesn't exists !")

copy_file = current_app.config["EXPORT_CONF_FOLDER"] + config[0]["url"]
config = from_xml_path(current_app, copy_file)

past_file = path.join(current_app.publish_path, "%s.xml" % id)

# add publish info in XML
if request.method == "GET":
config.xml.set("publish", "true")
edit_xml_string(config.meta, "relation", xml_publish_name)
message = "publish"

# add unpublish info in XML
if request.method == "DELETE":
config.xml.set("publish", "false")
edit_xml_string(config.meta, "relation", "")
remove(past_file)
message = "Unpublish"
past_file = None

# will update XML with correct relation value to map publish and draft files
config.write()

# commit to track this action
Expand All @@ -172,15 +195,10 @@ def publish_mviewer_config(id) -> Response:
# update JSON
config.register.update_from_id(id)

if path.exists(past_file):
remove(past_file)

# move to publish directory
if request.method == "GET":
copyfile(copy_file, past_file)

if request.method == "DELETE":
past_file=None
draft_file = current_app.config["CONF_PATH_FROM_MVIEWER"] + config.as_dict()["url"]
return jsonify({"online_file": past_file, "draft_file": draft_file})

Expand Down Expand Up @@ -212,7 +230,13 @@ def delete_config_workspace(id = None) -> Response:
if current_user.username == "anonymous" and config[0]["publisher"] != current_app.config["DEFAULT_ORG"]:
logger.debug("DELETE : NOT ALLOWED FOR THIS ANONYMOUS USER - ORG IS NOT DEFAULT")
return MethodNotAllowed("Not allowed !")

# delete publish
if "relation" in config[0] and config[0]["relation"]:
org_publish_dir = path.join(current_app.publish_path, current_user.organisation)
publish_file = path.join(org_publish_dir, "%s.xml" % config[0]["relation"])
if path.exists(publish_file):
remove(publish_file)

# delete in json
current_app.register.delete(id)
# delete dir
Expand Down
Loading

0 comments on commit ab9ed9e

Please sign in to comment.