diff --git a/app/controllers/import/index.js b/app/controllers/import/index.js new file mode 100644 index 0000000..e5c7833 --- /dev/null +++ b/app/controllers/import/index.js @@ -0,0 +1,45 @@ +import Controller from '@ember/controller'; +import { tracked } from '@glimmer/tracking'; +import { dropTask, timeout } from 'ember-concurrency'; +import { service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ImportController extends Controller { + @service templateFetcher; + @service router; + @tracked endpoint = 'https://reglementairebijlagen.lblod.info/'; + @tracked templateUri; + @tracked name; + @tracked type; + @tracked options; + typeOptions = [ + { + uri: 'http://data.lblod.info/vocabularies/gelinktnotuleren/ReglementaireBijlageTemplate', + label: 'Reglementaire Bijlage', + }, + { + uri: 'http://data.vlaanderen.be/ns/besluit#BehandelingVanAgendapunt', + label: 'Besluit', + }, + ]; + + @action + importUri(uri) { + this.router.transitionTo('import.uri', { + queryParams: { uri, endpoint: this.endpoint }, + }); + } + + @action + updateParam(field, event) { + this[field] = event.target.value; + } + connectToEndpoint = dropTask(async () => { + await timeout(400); + this.options = await this.templateFetcher.fetch.perform({ + endpoint: this.endpoint, + name: this.name, + type: this.type?.uri, + }); + }); +} diff --git a/app/controllers/import/uri.js b/app/controllers/import/uri.js new file mode 100644 index 0000000..eda8248 --- /dev/null +++ b/app/controllers/import/uri.js @@ -0,0 +1,23 @@ +import Controller from '@ember/controller'; +import { task } from 'ember-concurrency'; + +export default class ImportUriController extends Controller { + saveTemplate = task(async (event) => { + console.log('need to make sure to fetch template type first'); + return; + event.preventDefault(); + await this.editorDocument.save(); + const folder = await this.store.findRecord( + 'editor-document-folder', + this.templateTypeToCreate.folder, + ); + this.documentContainer.folder = folder; + await this.documentContainer.save(); + this.createTemplateModalIsOpen = false; + this.router.transitionTo( + 'template-management.edit', + this.documentContainer.id, + ); + }); + +} diff --git a/app/router.js b/app/router.js index c4178fd..983914c 100644 --- a/app/router.js +++ b/app/router.js @@ -32,4 +32,7 @@ Router.map(function () { this.route('edit-snippet', { path: '/:snippet_id/edit-snippet' }); }); }); + this.route('import', function () { + this.route('uri'); + }); }); diff --git a/app/routes/import.js b/app/routes/import.js new file mode 100644 index 0000000..3506be3 --- /dev/null +++ b/app/routes/import.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ImportRoute extends Route {} diff --git a/app/routes/import/uri.js b/app/routes/import/uri.js new file mode 100644 index 0000000..ee417e7 --- /dev/null +++ b/app/routes/import/uri.js @@ -0,0 +1,28 @@ +import Route from '@ember/routing/route'; +import { service } from '@ember/service'; + +export default class ImportUriRoute extends Route { + @service templateFetcher; + @service store; + + queryParams = { + uri: { refreshModel: true }, + endpoint: { refreshModel: true }, + }; + + async model(params) { + const template = await this.templateFetcher.fetchByUri(params); + await template.loadBody(); + const container = this.store.createRecord('document-container'); + const editorDocument = this.store.createRecord('editor-document', { + content: template.body, + title: template.title, + createdOn: new Date(), + updatedOn: new Date(), + }); + console.log(template.body); + console.log(editorDocument.content); + container.currentVersion = editorDocument; + return container; + } +} diff --git a/app/services/template-fetcher.js b/app/services/template-fetcher.js new file mode 100644 index 0000000..95e0112 --- /dev/null +++ b/app/services/template-fetcher.js @@ -0,0 +1,187 @@ +import Service, { service } from '@ember/service'; +import { tracked } from '@glimmer/tracking'; +import { task } from 'ember-concurrency'; + +export default class TemplateFetcher extends Service { + @service session; + @service store; + + @tracked account; + @tracked user; + @tracked group; + @tracked roles = []; + + fetchByUri = async ({ uri, endpoint }) => { + const fileEndpoint = `${endpoint}/files`; + const sparqlEndpoint = `${endpoint}/sparql`; + + const sparqlQuery = ` + PREFIX mu: + PREFIX pav: + PREFIX dct: + PREFIX schema: + PREFIX ext: + SELECT + ?template_version + ?title + ?fileId + (GROUP_CONCAT(?context;SEPARATOR="|") as ?contexts) + (GROUP_CONCAT(?disabledInContext;SEPARATOR="|") as ?disabledInContexts) + WHERE { + <${uri}> mu:uuid ?uuid; + pav:hasCurrentVersion ?template_version. + ?template_version mu:uuid ?fileId; + dct:title ?title. + OPTIONAL { + ?template_version schema:validThrough ?validThrough. + } + OPTIONAL { + ?template_version ext:context ?context. + } + OPTIONAL { + ?template_version ext:disabledInContext ?disabledInContext. + } + FILTER( ! BOUND(?validThrough) || ?validThrough > NOW()) + } + GROUP BY ?template_version ?title ?fileId + ORDER BY LCASE(REPLACE(STR(?title), '^ +| +$', '')) + `; + + const response = await this.sendQuery(sparqlEndpoint, sparqlQuery); + if (response.status === 200) { + const json = await response.json(); + const bindings = json.results.bindings; + const templates = bindings.map(this.bindingToTemplate(fileEndpoint)); + return templates[0]; + } else { + return null; + } + }; + + fetch = task(async ({ endpoint, name, type }) => { + const fileEndpoint = `${endpoint}/files`; + const sparqlEndpoint = `${endpoint}/sparql`; + const sparqlQuery = ` + PREFIX mu: + PREFIX pav: + PREFIX dct: + PREFIX schema: + PREFIX ext: + SELECT DISTINCT + ?template + ?template_version + ?title + ?fileId + ?type + (GROUP_CONCAT(?context;SEPARATOR="|") as ?contexts) + (GROUP_CONCAT(?disabledInContext;SEPARATOR="|") as ?disabledInContexts) + WHERE { + VALUES ?type { +${ + type + ? `<${type}>` + : ` + + + ` +} +} + ?template a ?type; + mu:uuid ?uuid; + pav:hasCurrentVersion ?template_version. + ?template_version dct:title ?title. + ${name ? `FILTER (CONTAINS(?title,"${name}"))` : ''} + ?template_version mu:uuid ?fileId. + OPTIONAL { + ?template_version schema:validThrough ?validThrough. + } + OPTIONAL { + ?template_version ext:context ?context. + } + OPTIONAL { + ?template_version ext:disabledInContext ?disabledInContext. + } + FILTER( ! BOUND(?validThrough) || ?validThrough > NOW()) + } + GROUP BY ?template ?template_version ?title ?fileId ?type + ORDER BY LCASE(REPLACE(STR(?title), '^ +| +$', '')) + `; + const response = await this.sendQuery(sparqlEndpoint, sparqlQuery); + if (response.status === 200) { + const json = await response.json(); + const bindings = json.results.bindings; + const templates = bindings.map(this.bindingToTemplate(fileEndpoint)); + return templates; + } else { + return []; + } + }); + + /** + * @param {string} sparqlQuery + * @returns {string} + */ + queryToFormBody(sparqlQuery) { + const details = { + query: sparqlQuery, + format: 'application/json', + }; + let formBody = []; + for (const property in details) { + const encodedKey = encodeURIComponent(property); + const encodedValue = encodeURIComponent(details[property]); + formBody.push(encodedKey + '=' + encodedValue); + } + formBody = formBody.join('&'); + return formBody; + } + bindingToTemplate(fileEndpoint) { + return (binding) => { + let type; + if ( + binding.type?.value === + 'http://data.lblod.info/vocabularies/gelinktnotuleren/ReglementaireBijlageTemplate' + ) + type = 'Reglementaire Bijlage'; + else if ( + binding.type?.value === + 'http://data.vlaanderen.be/ns/besluit#BehandelingVanAgendapunt' + ) + type = 'Behandeling van Agendapunt'; + else type = binding.type?.value; + return { + title: binding.title?.value, + type, + uri: binding.template?.value, + loadBody: async function () { + const response = await fetch( + `${fileEndpoint}/${binding.fileId.value}/download`, + ); + this.body = await response.text(); + }, + contexts: binding.contexts.value + ? binding.contexts.value.split('|') + : [], + disabledInContexts: binding.disabledInContexts.value + ? binding.disabledInContexts.value.split('|') + : [], + }; + }; + } + /** + * @param {string} endpoint + * @param {string} sparqlQuery + * @returns {Promise} + */ + async sendQuery(endpoint, sparqlQuery) { + const formBody = this.queryToFormBody(sparqlQuery); + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', + }, + body: formBody, + }); + return response; + } +} diff --git a/app/templates/import.hbs b/app/templates/import.hbs new file mode 100644 index 0000000..e2147ca --- /dev/null +++ b/app/templates/import.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/app/templates/import/index.hbs b/app/templates/import/index.hbs new file mode 100644 index 0000000..26916b1 --- /dev/null +++ b/app/templates/import/index.hbs @@ -0,0 +1,85 @@ +{{page-title "Import"}} + + Import + +
+
+
+ + Import manager + +
+
+ + + Endpoint + + + + + + Template Type + + + {{singleselect.label}} + + + + + Template Name + + + + +
+ + Search + +
+
+ + {{#if this.options.length}} + + <:header> + TitleType + + <:body> + {{#each this.options as |option|}} + + + + {{option.title}}
+ {{option.uri}} +
+ + {{option.type}} + + {{/each}} + +
+ {{/if}} +
+
+
diff --git a/app/templates/import/uri.hbs b/app/templates/import/uri.hbs new file mode 100644 index 0000000..40dd6fd --- /dev/null +++ b/app/templates/import/uri.hbs @@ -0,0 +1,39 @@ +{{page-title "Import template"}} + +
+ Import as new template +
+ +
+ + + + {{this.model.currentVersion.title}} + + + +
+ {{{this.model.currentVersion.htmlSafeContent}}} +
+
+
+
+
+ + + + Snippets + + + + TODO + + +
+
+
+
\ No newline at end of file diff --git a/app/templates/index.hbs b/app/templates/index.hbs index 430773d..35e5b23 100644 --- a/app/templates/index.hbs +++ b/app/templates/index.hbs @@ -74,6 +74,27 @@ +
  • + + + + {{t "landing-page.import.title"}} + + + +

    + {{t "landing-page.import.content"}} +

    +
    + + + {{t "landing-page.import.button"}} + + +
    +
  • diff --git a/translations/en-us.yaml b/translations/en-us.yaml index 3ddcbef..131fe29 100644 --- a/translations/en-us.yaml +++ b/translations/en-us.yaml @@ -76,6 +76,10 @@ landing-page: title: Snippets content: Manage snippets that are used in a template button: Go to snippets + import: + title: Import + content: Import templates from another environment + button: Go to import manager legal: accessibility-statement: Accessibility statement cookie-statement: Cookie statement diff --git a/translations/nl-BE.yaml b/translations/nl-BE.yaml index c676838..ca7df22 100644 --- a/translations/nl-BE.yaml +++ b/translations/nl-BE.yaml @@ -76,6 +76,10 @@ landing-page: title: Fragmentenlijsten content: Beheer fragmentenlijsten die gebruikt worden in sjablonen button: Ga naar fragmentenlijsten + import: + title: Import + content: Importeer templates uit een andere omgeving + button: Ga naar import manager legal: accessibility-statement: Toegankelijkheidsverklaring cookie-statement: Cookieverklaring