refactoring ui as single angularJs application

This commit is contained in:
Michele Artini 2023-01-12 15:09:14 +01:00
parent 2ed71532db
commit 5b681ed9e8
32 changed files with 1174 additions and 1349 deletions

View File

@ -0,0 +1,30 @@
package eu.dnetlib.is;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.data.is.resource.model.ResourceType;
import eu.dnetlib.data.is.resource.repository.ResourceTypeRepository;
@RestController
public class MainAjaxController {
@Autowired
private ResourceTypeRepository resourceTypeRepository;
@GetMapping("/ajax/resourceTypes")
public Iterable<ResourceType> resourceTypes() {
return resourceTypeRepository.findAll();
}
@GetMapping("/ajax/resourceTypes/{id}")
public ResourceType resourceTypes(@PathVariable final String id) {
return resourceTypeRepository
.findById(id)
.orElse(new ResourceType("not_present", "???", MediaType.TEXT_PLAIN_VALUE, 0));
}
}

View File

@ -1,98 +0,0 @@
package eu.dnetlib.is;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;
import eu.dnetlib.data.is.context.repository.ContextRepository;
import eu.dnetlib.data.is.resource.model.ResourceType;
import eu.dnetlib.data.is.resource.repository.ResourceTypeRepository;
import eu.dnetlib.data.is.vocabulary.repository.VocabularyRepository;
import eu.dnetlib.data.is.vocabulary.repository.VocabularyTermRepository;
import eu.dnetlib.is.info.KeyValue;
import eu.dnetlib.is.wfs.WfHistoryAjaxController;
import eu.dnetlib.openaire.dsm.utils.DsmBrowsableFields;
@Controller
public class MainController {
@Autowired
private ContextRepository contextRepository;
@Autowired
private VocabularyRepository vocabularyRepository;
@Autowired
private VocabularyTermRepository vocabularyTermRepository;
@Autowired
private ResourceTypeRepository resourceTypeRepository;
@GetMapping("/main")
public void mainPage() {}
@GetMapping("/dsm")
public void searchDsApi(final ModelMap map) {
map.addAttribute("pageSize", 100);
map.addAttribute("browsableFields", Arrays.stream(DsmBrowsableFields.values())
.map(f -> new KeyValue(f.name(), f.desc))
.collect(Collectors.toList()));
map.addAttribute("protocols", vocabularyTermRepository.findTermsByVocabulary("dnet:protocols"));
map.addAttribute("compatibilityLevels", vocabularyTermRepository.findTermsByVocabulary("dnet:compatibilityLevel"));
map.addAttribute("contentDescTypes", vocabularyTermRepository.findTermsByVocabulary("dnet:content_description_typologies"));
}
@GetMapping("/resources")
public String listResources(@RequestParam final String type, final ModelMap map) {
if (type.equalsIgnoreCase("vocabulary")) {
return "redirect:vocs";
} else if (type.equalsIgnoreCase("context")) {
return "redirect:contexts";
} else if (type.equalsIgnoreCase("protocol")) {
return "redirect:protocols";
} else {
final Optional<ResourceType> restype = resourceTypeRepository.findById(type);
if (restype.isPresent() && restype.get().isSimple()) {
map.addAttribute("type", restype.get());
} else {
map.addAttribute("type", new ResourceType("not_present", "???", MediaType.TEXT_PLAIN_VALUE, 0));
}
return "simpleResources";
}
}
@GetMapping("/vocs")
public void vocabularies() {}
@GetMapping("/contexts")
public void contexts() {}
@GetMapping("/protocols")
public void protocols() {}
@GetMapping("/wf_history")
public void wfHistory(final ModelMap map,
@RequestParam(required = false, defaultValue = "-1") final Long from,
@RequestParam(required = false, defaultValue = "-1") final Long to) {
map.put("maxNumberOfRecentWfs", WfHistoryAjaxController.MAX_NUMBER_OF_RECENT_WFS);
map.put("fromDate", from);
map.put("toDate", to);
}
@GetMapping("/info")
public void wfHistory() throws Exception {}
@ModelAttribute("resTypes")
public Iterable<ResourceType> resourceTypes() {
return resourceTypeRepository.findAll();
}
}

View File

@ -22,12 +22,16 @@ public class WfHistoryAjaxController {
@Autowired @Autowired
private WfProcessExecutionRepository wfProcessExecutionRepository; private WfProcessExecutionRepository wfProcessExecutionRepository;
@Deprecated
public static final int MAX_NUMBER_OF_RECENT_WFS = 100; public static final int MAX_NUMBER_OF_RECENT_WFS = 100;
@GetMapping("/") @GetMapping("/")
public List<WfProcessExecution> history(@RequestParam(required = false) final Long from, @RequestParam(required = false) final Long to) { public List<WfProcessExecution> history(
@RequestParam(required = true) final int total,
@RequestParam(required = false) final Long from,
@RequestParam(required = false) final Long to) {
if (from == null && to == null) { if (from == null && to == null) {
return wfProcessExecutionRepository.findAll(PageRequest.of(0, MAX_NUMBER_OF_RECENT_WFS, Sort.by("endDate").descending())).toList(); return wfProcessExecutionRepository.findAll(PageRequest.of(0, total, Sort.by("endDate").descending())).toList();
} else if (from == null) { } else if (from == null) {
return wfProcessExecutionRepository.findByEndDateBetweenOrderByEndDateDesc(new Date(0), new Date(to)); return wfProcessExecutionRepository.findByEndDateBetweenOrderByEndDateDesc(new Date(0), new Date(to));
} else if (to == null) { } else if (to == null) {

View File

@ -1,6 +1,10 @@
package eu.dnetlib.openaire.dsm; package eu.dnetlib.openaire.dsm;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@ -13,10 +17,12 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.common.controller.AbstractDnetController; import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.data.is.vocabulary.repository.VocabularyTermRepository;
import eu.dnetlib.data.openaire.dsm.model.Api; import eu.dnetlib.data.openaire.dsm.model.Api;
import eu.dnetlib.data.openaire.dsm.model.BrowseTerm; import eu.dnetlib.data.openaire.dsm.model.BrowseTerm;
import eu.dnetlib.data.openaire.dsm.model.view.SimpleDsWithApis; import eu.dnetlib.data.openaire.dsm.model.view.SimpleDsWithApis;
import eu.dnetlib.is.errors.DsmException; import eu.dnetlib.is.errors.DsmException;
import eu.dnetlib.is.info.KeyValue;
import eu.dnetlib.openaire.dsm.utils.DsmBrowsableFields; import eu.dnetlib.openaire.dsm.utils.DsmBrowsableFields;
@RestController @RestController
@ -26,6 +32,25 @@ public class DsmAjaxController extends AbstractDnetController {
@Autowired @Autowired
private DsmService dsmService; private DsmService dsmService;
@Autowired
private VocabularyTermRepository vocabularyTermRepository;
@GetMapping("/browsableFields")
public List<KeyValue> browsableFields() {
return Arrays.stream(DsmBrowsableFields.values())
.map(f -> new KeyValue(f.name(), f.desc))
.collect(Collectors.toList());
}
@GetMapping("/validTerms")
public Map<String, Iterable<String>> validTerms() {
final Map<String, Iterable<String>> map = new LinkedHashMap<>();
map.put("protocols", vocabularyTermRepository.findTermsByVocabulary("dnet:protocols"));
map.put("compatibilityLevels", vocabularyTermRepository.findTermsByVocabulary("dnet:compatibilityLevel"));
map.put("contentDescTypes", vocabularyTermRepository.findTermsByVocabulary("dnet:content_description_typologies"));
return map;
}
@GetMapping("/browse/{field}") @GetMapping("/browse/{field}")
public List<BrowseTerm> browse(@PathVariable final String field) { public List<BrowseTerm> browse(@PathVariable final String field) {
return dsmService.browseTerm(DsmBrowsableFields.valueOf(field)); return dsmService.browseTerm(DsmBrowsableFields.valueOf(field));

View File

@ -1,3 +1,5 @@
<h1 class="ml-3 mb-4 mt-3">Context Editor</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@ -6,7 +8,7 @@
data-target="#showParametersModal" ng-click="loadContextParameters()">[show]</a> data-target="#showParametersModal" ng-click="loadContextParameters()">[show]</a>
</p> </p>
<p> <p>
<a class="btn btn-sm btn-info" href="#!/list">Return to contexts list</a> <a class="btn btn-sm btn-success" href="/api/contexts/{{ctxId}}" target="_blank">Download</a> <a class="btn btn-sm btn-info" href="#!/contexts">Return to contexts list</a> <a class="btn btn-sm btn-success" href="/api/contexts/{{ctxId}}" target="_blank">Download</a>
</p> </p>
<ul> <ul>

View File

@ -1,3 +1,4 @@
<h1 class="ml-3 mb-4 mt-3">Contexts</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@ -26,7 +27,7 @@
<td colspan="4" class="text-muted">no contexts</td> <td colspan="4" class="text-muted">no contexts</td>
</tr> </tr>
<tr ng-repeat="ctx in contexts|filter:ctxFilter"> <tr ng-repeat="ctx in contexts|filter:ctxFilter">
<th><a href="#!/edit/{{ctx.id}}">{{ctx.id}}</a></th> <th><a href="#!/context/{{ctx.id}}">{{ctx.id}}</a></th>
<td>{{ctx.label}}</td> <td>{{ctx.label}}</td>
<td>{{ctx.type}}</td> <td>{{ctx.type}}</td>
<td align="right"> <td align="right">

View File

@ -1,3 +1,5 @@
<h1 class="ml-3 mb-4 mt-3">Datasource Manager: Add API</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">

View File

@ -1,3 +1,5 @@
<h1 class="ml-3 mb-4 mt-3">Datasource Manager: API</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">

View File

@ -1,3 +1,5 @@
<h1 class="ml-3 mb-4 mt-3">Datasource Manager: Results</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
@ -5,7 +7,7 @@
<span></span><b>Number of results:</b> {{nResults}}<br /> <span></span><b>Number of results:</b> {{nResults}}<br />
<b>Page:</b> <span ng-if="nPages > 0">{{currPage + 1}} / {{nPages}}</span><span ng-if="nPages == 0">- / -</span> <b>Page:</b> <span ng-if="nPages > 0">{{currPage + 1}} / {{nPages}}</span><span ng-if="nPages == 0">- / -</span>
</p> </p>
<nav ng-show="nPages > 1"> <nav ng-show="nPages > 1">
<ul class="pagination small"> <ul class="pagination small">
<li class="page-item" ng-class="{'disabled' : currPage <= 0}"> <li class="page-item" ng-class="{'disabled' : currPage <= 0}">
@ -58,7 +60,7 @@
<td> <td>
<div ng-repeat="a in r.apis" ng-if="a.id"> <div ng-repeat="a in r.apis" ng-if="a.id">
<span class="monospaced"> <span class="monospaced">
<a href="#!/api?id={{a.id}}">{{a.id}}</a> <a href="#!/dsm/api?id={{a.id}}">{{a.id}}</a>
</span> </span>
<span class="badge badge-primary" title="protocol">{{a.protocol}}</span> <span class="badge badge-primary" title="protocol">{{a.protocol}}</span>
<span class="badge badge-info" title="compliance">{{a.compliance}}</span> <span class="badge badge-info" title="compliance">{{a.compliance}}</span>
@ -66,7 +68,7 @@
<span ng-if="a.aggrDate"><br/><b>Last aggregation:</b> {{a.aggrDate}} <b>(total: {{a.aggrTotal}})</b></span> <span ng-if="a.aggrDate"><br/><b>Last aggregation:</b> {{a.aggrDate}} <b>(total: {{a.aggrTotal}})</b></span>
</div> </div>
<a class="btn btn-primary btn-sm mt-2" href="#!/add/api?dsId={{r.id}}&amp;dsName={{r.name}}">add api</a> <a class="btn btn-primary btn-sm mt-2" href="#!/dsm/addApi?dsId={{r.id}}&amp;dsName={{r.name}}">add api</a>
</td> </td>
</tr><tr ng-if="r.consenttermsofuse"> </tr><tr ng-if="r.consenttermsofuse">
<th>Consent Terms of Use</th> <th>Consent Terms of Use</th>

View File

@ -1,3 +1,5 @@
<h1 class="ml-3 mb-4 mt-3">Datasource Manager: Search</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="row mt-5"> <div class="row mt-5">
<div class="offset-md-3 col-md-6 offset-lg-4 col-lg-4"> <div class="offset-md-3 col-md-6 offset-lg-4 col-lg-4">

View File

@ -0,0 +1,41 @@
<h1 class="ml-3 mb-4 mt-3">Container Info</h1>
<div class="container-fluid">
<div class="row">
<div class="col">
<p>
<input type="text" class="form-control form-control-sm" ng-model="infoFilter" placeholder="Filter..."/>
</p>
<div class="card mb-3" ng-repeat="section in info" ng-show="(section.data|filter:infoFilter).length > 0">
<div class="card-body">
<h5 class="card-title">{{section.name}}</h5>
<table class="table table-striped table-sm small" style="table-layout: fixed;">
<thead ng-if="section.name == 'Modules'">
<tr>
<th>Group ID</th>
<th>Artifact ID</th>
<th>Version</th>
<th>POM</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="r in section.data|filter:infoFilter" ng-if="section.name != 'Modules'">
<th style="width: 25%;">{{r.k}}</th>
<td style="overflow-x: auto;"><pre style="margin: 0;">{{r.v}}</pre></td>
</tr>
<tr ng-repeat="r in section.data|filter:infoFilter" ng-if="section.name == 'Modules'" ng-class="{'table-warning' : r.files.length > 1}">
<td>{{r.group}}</td>
<td>{{r.name}}</td>
<td class="text-monospace"><span ng-repeat="v in r.versions">{{v}}<br /></span></td>
<td class="text-monospace"><span ng-repeat="f in r.files">{{f}}<br /></span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,34 @@
<h1 class="ml-3 mb-4 mt-3">Harvesting Protocols</h1>
<div class="container-fluid">
<p><b>NOTE:</b> Use the api to register new protocols</p>
<div class="row">
<div class="col">
<div class="card mt-4" ng-repeat="prot in protocols">
<div class="card-body">
<h5 class="card-title">{{prot.id}}</h5>
<p class="card-text" ng-if="prot.params.length == 0">No parameters</p>
<table class="table table-sm table-striped" ng-if="prot.params.length > 0">
<thead>
<th style="width: 20%">Parameter</th>
<th>Description</th>
<th style="width: 15%" class="text-center">Type</th>
<th style="width: 15%" class="text-center">Required</th>
<th style="width: 15%" class="text-center">Has Sel Function</th>
</thead>
<tbody>
<tr ng-repeat="p in prot.params">
<th>{{p.name}}</th>
<td>{{p.label}}</td>
<td class="text-center">{{p.type}}</td>
<td class="text-center"><i class="fa fa-check" ng-if="!p.optional"></i></td>
<td class="text-center"><i class="fa fa-check" ng-if="p.hasSelFunction"></i></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,124 @@
<h1 class="ml-3 mb-4 mt-3">{{typeDesc.name}}</h1>
<div class="container-fluid">
<div class="row">
<div class="col">
<p>
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#newResourceModal" ng-click="prepareNewResource()">create a new resource</button>
</p>
<p ng-show="resources.length > 0">
<input type="text" class="form-control form-control-sm" ng-model="resFilter" placeholder="Filter..." />
</p>
<p>
<span class="text-muted"><b>Number of resources:</b> {{(resources | filter:resFilter).length}}</span>
</p>
<div class="card mb-4" ng-repeat="r in resources|filter:resFilter">
<div class="card-body small">
<span class="badge badge-primary float-right">{{typeDesc.contentType}}</span>
<h5 class="card-title" title="{{r.id}}">{{r.name}}</h5>
<p class="card-text">{{r.description}}</p>
<p class="text-muted small">
<b>Id:</b> {{r.id}}<br /> <b>Creation date:</b> {{r.creationDate}}<br /> <b>Modification date:</b> {{r.modificationDate}}
</p>
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#editMetadataModal" ng-click="prepareEditMetadata(r)">edit metadata</button>
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#editContentModal" ng-click="prepareEditContent(r)">edit content</button>
<a href="./api/resources/{{r.id}}/content" class="btn btn-sm btn-success" target="_blank">raw content</a>
<button type="button" class="btn btn-sm btn-danger" ng-click="deleteResource(r)">delete</button>
</div>
</div>
</div>
</div>
</div>
<!-- Modals -->
<div class="modal fade" tabindex="-1" id="editMetadataModal">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Edit metadata</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<form>
<div class="modal-body">
<div class="form-group">
<label>ID</label> <input type="text" readonly class="form-control-plaintext" ng-model="tmpRes.id" />
</div>
<div class="form-group">
<label>Type</label> <input type="text" readonly class="form-control-plaintext" ng-model="tmpRes.type" />
</div>
<div class="form-group">
<label>Name</label> <input type="text" class="form-control" ng-model="tmpRes.name" />
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" ng-model="tmpRes.description" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-sm btn-primary" data-dismiss="modal" ng-click="saveMetadata(tmpRes.id, tmpRes)" ng-disabled="!tmpRes.id || !tmpRes.name">Submit</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" id="newResourceModal">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">New resource ({{type}})</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<form>
<div class="modal-body">
<div class="form-group">
<label>Name</label> <input type="text" class="form-control" ng-model="tmpRes.name" />
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" ng-model="tmpRes.description"></textarea>
</div>
<div class="form-group">
<label>Content ({{typeDesc.contentType}})</label>
<textarea class="form-control" ng-model="tmpRes.content" rows="25"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-sm btn-primary" ng-click="createNewResource(tmpRes)" ng-disabled="!tmpRes.name || !tmpRes.content">Submit</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" id="editContentModal">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Edit content</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<form>
<div class="modal-body">
<div class="form-group">
<label>{{typeDesc.contentType}}</label>
<textarea class="form-control" ng-model="tmpContent" rows="25"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-sm btn-primary" ng-click="saveContent(tmpRes.id, tmpContent)" ng-disabled="!tmpContent">Submit</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,3 +1,5 @@
<h1 class="ml-3 mb-4 mt-3">Vocabulary Editor</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@ -7,7 +9,7 @@
<b>Description: </b>{{vocInfo.description}} <b>Description: </b>{{vocInfo.description}}
</p> </p>
<p> <p>
<a class="btn btn-sm btn-info" href="#!/list">Return to vocabulary list</a> <a class="btn btn-sm btn-info" href="#!/vocs">Return to vocabulary list</a>
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#editVocabularyTermModal" ng-click="prepareNewTerm()">create a new term</button> <button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#editVocabularyTermModal" ng-click="prepareNewTerm()">create a new term</button>
<a class="btn btn-sm btn-success" href="/api/vocs/{{vocId}}/terms" target="_blank">Download</a> <a class="btn btn-sm btn-success" href="/api/vocs/{{vocId}}/terms" target="_blank">Download</a>
</p> </p>

View File

@ -1,3 +1,5 @@
<h1 class="ml-3 mb-4 mt-3">Vocabularies</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@ -24,7 +26,7 @@
<td colspan="4" class="text-muted">no vocabularies</td> <td colspan="4" class="text-muted">no vocabularies</td>
</tr> </tr>
<tr ng-repeat="v in vocabularies|filter:vocFilter"> <tr ng-repeat="v in vocabularies|filter:vocFilter">
<th><a href="#!/edit?id={{v.id}}">{{v.id}}</a></th> <th><a href="#!/vocabularyEditor?id={{v.id}}">{{v.id}}</a></th>
<td>{{v.name}}</td> <td>{{v.name}}</td>
<td>{{v.description}}</td> <td>{{v.description}}</td>
<td align="right"> <td align="right">

View File

@ -0,0 +1,132 @@
<h1 class="ml-3 mb-4 mt-3">Workflow History</h1>
<div class="container-fluid">
<div class="row">
<div class="col">
<p ng-show="workflows.length > 0">
<input type="text" class="form-control form-control-sm" ng-model="wfFilter" placeholder="Filter..."/>
</p>
<p class="text-muted">
<span ng-show="fromDate < 0 && toDate < 0"><b>Recent workflows</b> (max {{maxNumberOfRecentWfs}})</span>
<span ng-show="fromDate >= 0 && toDate >= 0"><b>Workflows from </b>{{fromDate | date:"yyyy-MM-dd HH:mm:ss"}} <b>to</b> {{toDate | date:"yyyy-MM-dd HH:mm:ss"}}</span>
<span ng-show="fromDate >= 0 && toDate < 0"><b>Workflows from </b>{{fromDate | date:"yyyy-MM-dd HH:mm:ss"}} <b>to</b> <i>undefined</i></span>
<span ng-show="fromDate < 0 && toDate >= 0"><b>Workflows from </b><i>undefined</i> <b>to</b> {{toDate | date:"yyyy-MM-dd HH:mm:ss"}}</span>
<br />
<span><b>Count :</b> {{(workflows | filter:wfFilter).length}}</span>
</p>
<table class="table table-sm table-striped small">
<thead>
<tr>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'processId'; sortReverse = !sortReverse">
Process Id
<i ng-show="sortField == 'processId' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'processId' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 20%">
<a href="javascript:void(0)" ng-click="sortField = 'name'; sortReverse = !sortReverse">
Workflow Name
<i ng-show="sortField == 'name' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'name' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'family'; sortReverse = !sortReverse">
Workflow Family
<i ng-show="sortField == 'family' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'family' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 30%">
<a href="javascript:void(0)" ng-click="sortField = 'dsName'; sortReverse = !sortReverse">
Datasource
<i ng-show="sortField == 'dsName' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'dsName' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'status'; sortReverse = !sortReverse">
Status
<i ng-show="sortField == 'status' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'status' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'startDate'; sortReverse = !sortReverse">
Start Date
<i ng-show="sortField == 'startDate' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'startDate' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'endDate'; sortReverse = !sortReverse">
End Date
<i ng-show="sortField == 'endDate' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'endDate' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-show="(workflows|filter:wfFilter).length == 0">
<td colspan="7" class="text-muted">no workflows</td>
</tr>
<tr ng-repeat="wf in workflows | orderBy:sortField:sortReverse | filter:wfFilter">
<th><a href="javascript:void(0)" data-toggle="modal" data-target="#showWfDetailsModal" ng-click="setCurrentWf(wf)">{{wf.processId}}</a></th>
<td>{{wf.name}}</td>
<td>{{wf.family}}</td>
<td>{{wf.dsName}}</td>
<td><span class="badge" ng-class="{
'badge-success' : (wf.status == 'success'),
'badge-danger' : (wf.status == 'failure'),
'badge-primary' : (wf.status != 'success') && (wf.status != 'failure')
}">{{wf.status}}</span></td>
<td>{{wf.startDate}}</td>
<td>{{wf.endDate}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Modals -->
<div class="modal fade" tabindex="-1" id="showWfDetailsModal">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Details</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>
<b>Started at: </b>{{currentWf.startDate}}<br />
<b>Finished at: </b>{{currentWf.endDate}}<br />
<b>Duration: </b>{{currentWf.duration}}<br />
</p>
<input type="text" class="form-control form-control-sm" ng-model="detailsFilter" placeholder="Filter..."/>
<table class="table table-sm table-striped small mt-2" style="table-layout: fixed;">
<tr ng-repeat="p in currentWf.arrayDetails | filter:detailsFilter">
<th style="width: 30%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"><a href="javascript:void(0)" ng-click="setCurrentDetailParam(p.k,p.v)">{{p.k}}</a></th>
<td style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{p.v}}</td>
</tr>
</table>
<div class="card card-body bg-light" ng-show="currDetailsKey">
<b>{{currDetailsKey}}</b><br />
<pre>{{currDetailsValue}}</pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

View File

@ -1,9 +1,111 @@
<!DOCTYPE html> <!DOCTYPE HTML>
<html>
<html ng-app="isApp">
<head> <head>
<title>OpenAIRE IS Tools</title> <title>OpenAIRE Tools</title>
<meta http-equiv="refresh" content="0; URL='./main'" /> <link rel="stylesheet" href="common/css/bootstrap.cerulean.min.css" />
<link rel="stylesheet" href="common/css/fontawesome-all.min.css" />
<style type="text/css">
td,th {
vertical-align: middle !important;
}
label {
font-weight: bold;
}
.overlaydiv {
position: fixed;
width: 100%;
height: 100%;
z-index: 10000;
visibility: hidden;
}
.grayRectangle {
position: absolute;
background-color: black;
opacity:0.6;
top: 30%;
left: 40%;
width: 20%;
height: 20%;
z-index: 100;
border-radius: 15px;
}
</style>
</head> </head>
<body>
<div id="spinnerdiv" class="overlaydiv">
<span class="grayRectangle"><!--The spinner is added on loading here--></span>
</div>
<nav class="navbar navbar-expand-lg navbar-light bg-light" ng-controller="isMenuController">
<a class="navbar-brand" href="./">D-Net</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="./">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Datasources</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/dsm/search">Search</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Configuration</a>
<div class="dropdown-menu">
<h6 class="dropdown-header">Simple resources</h6>
<a ng-repeat="t in resTypes" ng-if="t.simple"
class="dropdown-item"
href="#!/resources/{{t.id}}">
<span>{{t.name}}</span>
<span class="badge badge-primary">{{t.count}}</span>
</a>
<div class="dropdown-divider"></div>
<h6 class="dropdown-header">Advanced resources</h6>
<a ng-repeat="t in resTypes" ng-if="!t.simple"
class="dropdown-item"
href="#!/resources/{{t.id}}">
<span>{{t.name}}</span>
<span class="badge badge-primary">{{t.count}}</span>
</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Logs</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/wf_history/100">Workflow history</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Info</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/info">Container Info</a>
<a class="dropdown-item" href="./docs" target="_blank">API documentation</a>
</div>
</li>
</ul>
</div>
</nav>
<div ng-view></div>
</body>
<script src="common/js/jquery.min.js"></script>
<script src="common/js/popper.min.js"></script>
<script src="common/js/bootstrap.min.js"></script>
<script src="common/js/angular.min.js"></script>
<script src="common/js/angular-route.min.js"></script>
<script src="common/js/spin.js"></script>
<script src="js/is_utils.js"></script>
<script src="js/is_main.js"></script>
</html> </html>

View File

@ -1,107 +0,0 @@
var app = angular.module('contextsApp', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/list', { templateUrl: 'contexts/list.html', controller: 'ctxListController' })
.when('/edit/:id', { templateUrl: 'contexts/editor.html', controller: 'ctxEditorController' })
.otherwise({ redirectTo: '/list' });
}
]);
app.controller('ctxListController', function($scope, $http) {
$scope.contexts = [];
$scope.tmpCtx = {};
$scope.mode = '';
call_http_get($http, './ajax/contexts/?' + $.now(), function(res) {
$scope.contexts = res.data;
});
$scope.prepareNewCtx = function() {
$scope.mode = 'new';
$scope.tmpCtx = {
'id' : '',
'label' : '',
'type' : '',
'parameters' : []
};
}
$scope.prepareEditCtx = function(ctx) {
$scope.mode = 'edit';
$scope.tmpCtx = angular.copy(ctx);
}
$scope.saveContext = function(ctx) {
if ($scope.mode == 'new') {
var found = false;
angular.forEach($scope.contexts, function(v) {
if (ctx.id == ctx.id) { found = true; };
});
if (found) {
alert("Insertion failed: context already exists !");
return;
}
}
json_http_post($http,'./ajax/contexts/?' + $.now(), ctx, function(res) {
$scope.contexts = res.data;
alert("Context saved");
});
};
$scope.deleteContext = function(id) {
if (confirm("Are you sure ?")) {
call_http_delete($http, './ajax/contexts/' + encodeURIComponent(id) + '?' + $.now(), function(res) {
$scope.contexts = res.data;
alert("Context deleted");
});
}
};
});
app.controller('ctxEditorController', function($scope, $http, $location, $routeParams) {
$scope.ctxId = $routeParams.id;
$scope.ctxInfo = {};
$scope.categories = [];
$scope.parameters = [];
var url = './ajax/contexts/' + encodeURIComponent($scope.ctxId);
call_http_get($http, url + '?' + $.now(), function(res) {
$scope.ctxInfo = res.data;
call_http_get($http, url + '/categories?' + $.now(), function(res) {
$scope.categories = res.data;
});
});
$scope.loadContextParameters = function() {
$scope.parameters = [];
call_http_get($http, './ajax/contexts/' + encodeURIComponent($scope.ctxId) + '?' + $.now(), function(res) {
$scope.parameters = res.data.parameters;
});
}
$scope.populateNode = function(level, node) {
$scope.url = './ajax/contexts/'
+ encodeURIComponent(level)
+ '/'
+ encodeURIComponent(node.id)
+ '/concepts';
node.populated = true;
call_http_get($http, $scope.url + '?' + $.now(), function(res) {
node.concepts = res.data;
});
}
$scope.initShowParameters = function(params) {
$scope.parameters = params;
}
});

View File

@ -0,0 +1,582 @@
var app = angular.module('isApp', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/info', { templateUrl: './html/info.html', controller: 'infoController' })
.when('/wf_history/:total', { templateUrl: './html/wf_history.html', controller: 'wfHistoryController' })
.when('/protocols', { templateUrl: './html/protocols.html', controller: 'protocolsController' })
.when('/resources/:type', { templateUrl: './html/resources.html', controller: 'resourcesController' })
.when('/contexts', { templateUrl: './html/contexts.html', controller: 'ctxListController' })
.when('/context/:id', { templateUrl: './html/context_viewer.html', controller: 'ctxViewerController' })
.when('/vocs', { templateUrl: './html/vocs.html', controller: 'vocListController' })
.when('/vocabularyEditor', { templateUrl: './html/voc_editor.html', controller: 'vocEditorController' })
.when('/dsm/search', { templateUrl: './html/dsm_search.html', controller: 'dsmSearchController' })
.when('/dsm/results/:page/:size', { templateUrl: './html/dsm_results.html', controller: 'dsmResultsController' })
.when('/dsm/results/:field/:page/:size', { templateUrl: './html/dsm_results.html', controller: 'dsmResultsController' })
.when('/dsm/addApi', { templateUrl: './html/dsm_add_api.html', controller: 'dsmAddApiController' })
.when('/dsm/api', { templateUrl: './html/dsm_api.html', controller: 'dsmApiController' })
.otherwise({ redirectTo: '/info' });
}
]);
app.controller('isMenuController', function($scope, $http) {
$scope.resTypes = [];
call_http_get($http, './ajax/resourceTypes' , function(res) {
$scope.resTypes = res.data;
});
});
// ----------------------------------------------------
app.controller('infoController', function($scope, $http) {
$scope.info = [];
call_http_get($http, './ajax/info/?' + $.now(), function(res) {
angular.forEach(res.data, function(section) {
if (section.name != 'Modules') {
angular.forEach(section.data, function(r) {
if (r.k.toLowerCase().endsWith('path') || r.k.toLowerCase().endsWith('.dirs')) {
r.v = r.v.replaceAll(':', ':\n');
}
});
}
});
$scope.info = res.data;
});
});
// ----------------------------------------------------
app.controller('wfHistoryController', function($scope, $http, $routeParams) {
$scope.fromDate = $routeParams.from;
$scope.toDate = $routeParams.to;
$scope.maxNumberOfRecentWfs = $routeParams.total;
$scope.workflows = [];
$scope.currentWf = {};
$scope.currDetailsKey = '';
$scope.currDetailsValue = '';
$scope.sortField = 'processId';
$scope.sortReverse = false;
$scope.reload = function() {
var url = './ajax/wfs/?' + $.now();
url += "&total=" + $scope.maxNumberOfRecentWfs;
if ($scope.fromDate > 0) { url += "&from=" + $scope.fromDate; }
if ($scope.toDate > 0) { url += "&to=" + $scope.toDate; }
call_http_get($http, url, function(res) {
$scope.workflows = res.data;
});
};
$scope.setCurrentWf = function(wf) {
$scope.currentWf = angular.copy(wf);
$scope.currDetailsKey = '';
$scope.currDetailsValue = '';
$scope.currentWf.arrayDetails = [];
$scope.currentWf.duration = $scope.calculateDateDiff(parseInt(wf.details['system:startDate']), parseInt(wf.details['system:endDate']));
angular.forEach(wf.details, function(v,k) {
$scope.currentWf.arrayDetails.push({'k':k, 'v':v});
});
}
$scope.setCurrentDetailParam = function(k, v) {
$scope.currDetailsKey = k;
$scope.currDetailsValue = v;
}
$scope.calculateDateDiff = function(start, end) {
if (start <= 0 || end <= 0) {
return '-';
}
var seconds = 0;
var minutes = 0;
var hours = 0;
var days = 0;
if (end > start) {
seconds = Math.round((end - start) / 1000);
if (seconds > 60) {
minutes = Math.floor(seconds / 60);
seconds = seconds % 60;
if (minutes > 60) {
hours = Math.floor(minutes / 60);
minutes = minutes % 60;
if (hours > 24) {
days = Math.floor(hours / 24);
hours = hours % 24;
}
}
}
}
var res = '';
if (days > 0) {
if (res) { res += ', '; }
res += days + " day(s)"
}
if (hours > 0) {
if (res) { res += ', '; }
res += hours + " hour(s)"
}
if (minutes > 0) {
if (res) { res += ', '; }
res += minutes + " minute(s)"
}
if (seconds > 0) {
if (res) { res += ', '; }
res += seconds + " second(s)"
}
if (!res) {
res = '0 seconds';
}
return res;
}
$scope.reload();
});
// ----------------------------------------------------
app.controller('resourcesController', function($scope, $http, $routeParams, $location) {
$scope.resources = [];
$scope.tmpRes = {};
$scope.tmpContent = "loading...";
$scope.type = $routeParams.type;
$scope.typeDesc = {};
$scope.reload = function() {
call_http_get($http, './ajax/resourceTypes/' + encodeURIComponent($scope.type) + '?' + $.now(), function(res) {
$scope.typeDesc = res.data;
});
call_http_get($http, './ajax/resources/' + encodeURIComponent($scope.type) + '?' + $.now(), function(res) {
$scope.resources = res.data;
});
};
$scope.prepareNewResource = function() {
$scope.tmpRes = {
'name' : '',
'description' : '',
'content' : ''
};
}
$scope.prepareEditMetadata = function(r) {
$scope.tmpRes = angular.copy(r);
}
$scope.prepareEditContent = function(r) {
$scope.tmpRes = angular.copy(r);
$scope.tmpContent = "loading...";
call_http_get($http, './ajax/resources/' + encodeURIComponent(r.id) + '/content?' + $.now(), function(res) {
if (res.data instanceof Object) {
$scope.tmpContent = JSON.stringify(res.data, null, "\t");
} else {
$scope.tmpContent = res.data;
}
});
}
$scope.createNewResource = function(r) {
params_http_post($http, './ajax/resources/?' + $.now(), $.param({
'name' : r.name,
'type' : $scope.type,
'description' : r.description,
'content' : r.content
}), function(res) {
alert("Resource saved");
$('#newResourceModal').modal('hide');
$scope.reload();
});
}
$scope.saveMetadata = function(id, md) {
json_http_post($http, './ajax/resources/' + encodeURIComponent(id) + '/metadata?' + $.now(), md, function(res) {
alert("Resource saved");
$scope.reload();
});
};
$scope.saveContent = function(id, content) {
params_http_post($http, './ajax/resources/' + encodeURIComponent(id) + '/content?' + $.now(), $.param({
'content' : content
}), function(res) {
alert("Resource saved");
$('#editContentModal').modal('hide');
});
};
$scope.deleteResource = function(r) {
if (confirm("Are you sure ?")) {
call_http_delete($http, './ajax/resources/' + encodeURIComponent(r.id) + '?' + $.now(), function(res) {
alert("Resource deleted");
$scope.reload();
});
}
};
if ($scope.type == 'context') { $location.url("/contexts"); }
else if ($scope.type == 'vocabulary') { $location.url("/vocs"); }
else if ($scope.type == 'protocol') { $location.url("/protocols"); }
else { $scope.reload(); }
});
// ----------------------------------------------------
app.controller('protocolsController', function($scope, $http) {
$scope.protocols = [];
call_http_get($http, './ajax/protocols/?' + $.now(), function(res) {
$scope.protocols = res.data;
});
});
// ----------------------------------------------------
app.controller('ctxListController', function($scope, $http) {
$scope.contexts = [];
$scope.tmpCtx = {};
$scope.mode = '';
call_http_get($http, './ajax/contexts/?' + $.now(), function(res) {
$scope.contexts = res.data;
});
$scope.prepareNewCtx = function() {
$scope.mode = 'new';
$scope.tmpCtx = {
'id' : '',
'label' : '',
'type' : '',
'parameters' : []
};
}
$scope.prepareEditCtx = function(ctx) {
$scope.mode = 'edit';
$scope.tmpCtx = angular.copy(ctx);
}
$scope.saveContext = function(ctx) {
if ($scope.mode == 'new') {
var found = false;
angular.forEach($scope.contexts, function(v) {
if (ctx.id == ctx.id) { found = true; };
});
if (found) {
alert("Insertion failed: context already exists !");
return;
}
}
json_http_post($http,'./ajax/contexts/?' + $.now(), ctx, function(res) {
$scope.contexts = res.data;
alert("Context saved");
});
};
$scope.deleteContext = function(id) {
if (confirm("Are you sure ?")) {
call_http_delete($http, './ajax/contexts/' + encodeURIComponent(id) + '?' + $.now(), function(res) {
$scope.contexts = res.data;
alert("Context deleted");
});
}
};
});
// ----------------------------------------------------
app.controller('ctxViewerController', function($scope, $http, $routeParams) {
$scope.ctxId = $routeParams.id;
$scope.ctxInfo = {};
$scope.categories = [];
$scope.parameters = [];
var url = './ajax/contexts/' + encodeURIComponent($scope.ctxId);
call_http_get($http, url + '?' + $.now(), function(res) {
$scope.ctxInfo = res.data;
call_http_get($http, url + '/categories?' + $.now(), function(res) {
$scope.categories = res.data;
});
});
$scope.loadContextParameters = function() {
$scope.parameters = [];
call_http_get($http, './ajax/contexts/' + encodeURIComponent($scope.ctxId) + '?' + $.now(), function(res) {
$scope.parameters = res.data.parameters;
});
}
$scope.populateNode = function(level, node) {
$scope.url = './ajax/contexts/'
+ encodeURIComponent(level)
+ '/'
+ encodeURIComponent(node.id)
+ '/concepts';
node.populated = true;
call_http_get($http, $scope.url + '?' + $.now(), function(res) {
node.concepts = res.data;
});
}
$scope.initShowParameters = function(params) {
$scope.parameters = params;
}
});
// ----------------------------------------------------
app.controller('vocListController', function($scope, $http) {
$scope.vocabularies = [];
$scope.tmpVoc = {};
$scope.mode = '';
call_http_get($http, './ajax/vocs/?' + $.now(), function(res) {
$scope.vocabularies = res.data;
});
$scope.prepareNewVoc = function() {
$scope.mode = 'new';
$scope.tmpVoc = {
'id' : '',
'name' : '',
'description' : ''
};
}
$scope.prepareEditVoc = function(voc) {
$scope.mode = 'edit';
$scope.tmpVoc = angular.copy(voc);
}
$scope.saveVocabulary = function(voc) {
if ($scope.mode == 'new') {
var found = false;
angular.forEach($scope.vocabularies, function(v) {
if (voc.id == v.id) { found = true; };
});
if (found) {
alert("Insertion failed: vocabulary already exists !");
return;
}
}
json_http_post($http, './ajax/vocs/?' + $.now(), voc, function(res) {
$scope.vocabularies = res.data;
alert("Vocabulary saved");
});
};
$scope.deleteVocabulary = function(id) {
if (confirm("Are you sure ?")) {
call_http_delete($http, './ajax/vocs/' + encodeURIComponent(id) + '?' + $.now(), function(res) {
$scope.vocabularies = res.data;
alert("Vocabulary deleted");
});
}
};
});
// ----------------------------------------------------
app.controller('vocEditorController', function($scope, $http, $routeParams) {
$scope.terms = [];
$scope.vocId = $routeParams.id;
$scope.vocInfo = {};
$scope.editTermCode = '';
$scope.tmpTerm = {};
$scope.mode = '';
$scope.currTerm = [];
$scope.baseUrl = './ajax/vocs/' + encodeURIComponent($scope.vocId);
call_http_get($http, $scope.baseUrl + '?' + $.now(), function(res) {
$scope.vocInfo = res.data;
call_http_get($http, $scope.baseUrl + '/terms?' + $.now(), function(res) {
$scope.terms = res.data;
});
});
$scope.setCurrTerm = function(term) {
$scope.currTerm = angular.copy(term);
}
$scope.prepareNewTerm = function() {
$scope.mode = 'new';
$scope.editTermCode = '';
$scope.tmpTerm = {
'code' : '',
'name' : '',
'encoding' : 'OPENAIRE',
'synonyms' : []
};
}
$scope.prepareEditTerm = function(term) {
$scope.mode = 'edit';
$scope.editTermCode = term.code;
$scope.tmpTerm = angular.copy(term);
}
$scope.saveTerm = function(term) {
var url = $scope.baseUrl + '/terms?' + $.now();
json_http_post($http, url, term, function(res) {
if ($scope.editTermCode != '' && $scope.editTermCode != $scope.tmpTerm.code) {
var deleteUrl = $scope.baseUrl + '/terms/' + encodeURIComponent($scope.editTermCode) + '?' + $.now();
call_http_delete($http, deleteUrl, function(res) {
$scope.terms = res.data;
alert("Term replaced");
});
} else {
$scope.terms = res.data;
alert("Term saved");
}
});
};
$scope.deleteTerm = function(code) {
if (confirm("Are you sure ?")) {
var url = $scope.baseUrl + '/terms/' + encodeURIComponent(code) + '?' + $.now();
call_http_delete($http, url, function(res) {
$scope.terms = res.data;
alert("Term deleted");
});
}
};
});
// ----------------------------------------------------
app.controller('dsmSearchController', function($scope, $http, $location, $timeout) {
$scope.browseFieldId = "";
$scope.browseFieldName = "";
$scope.browseData = [];
$scope.browsableFields = [];
call_http_get($http, './ajax/dsm/browsableFields' , function(res) {
$scope.browsableFields = res.data;
});
$scope.browseField = function(id, label) {
$scope.browseFieldId = id;
$scope.browseFieldName = name;
$scope.browseData = [];
call_http_get($http, './ajax/dsm/browse/' + encodeURIComponent(id) + '?' + $.now(), function(res) {
$scope.browseData = res.data;
});
}
$scope.search = function(field, value) {
var path = "/dsm/results";
if (field) { path += '/' + encodeURIComponent(field); }
path += '/0/100';
$timeout(function() {
$location.path(path).search('value', value);
}, 1000);
}
});
// ----------------------------------------------------
app.controller('dsmResultsController', function($scope, $http, $location, $routeParams) {
$scope.field = $routeParams.field;
$scope.value = $routeParams.value;
$scope.pageSize = $routeParams.size;
$scope.currPage = $routeParams.page;
$scope.nResults = 0;
$scope.results = [];
$scope.nPages = 0;
var url = './ajax/dsm/';
if ($scope.field) { url += 'searchByField/' + encodeURIComponent($scope.field); }
else { url += 'search' }
url += '/' + $scope.currPage + '/' + $scope.pageSize;
url += '?value=' + encodeURIComponent($scope.value) + '&' + $.now();
call_http_get($http, url, function(res) {
$scope.results = res.data.content;
$scope.nResults = res.data.totalElements;
$scope.currPage = res.data.number;
$scope.nPages = res.data.totalPages;
});
$scope.gotoPage = function(page) {
$scope.results = [];
var path = "/dsm/results";
if ($scope.field) { path += '/' + encodeURIComponent($scope.field); }
path += '/' + page + '/' + $scope.pageSize;
$location.path(path).search('value', $scope.value);
}
});
// ----------------------------------------------------
app.controller('dsmApiController', function($scope, $http, $routeParams) {
$scope.apiId = $routeParams.id;
$scope.api = {};
call_http_get($http, './ajax/dsm/api?id=' + encodeURIComponent($scope.apiId) , function(res) {
$scope.api = res.data;
});
});
// ----------------------------------------------------
app.controller('dsmAddApiController', function($scope, $http, $routeParams) {
$scope.dsName = $routeParams.dsName;
$scope.prefix = 'api_________::' + $routeParams.dsId + '::';
$scope.api = {}
$scope.protocols = [];
$scope.compatibilityLevels = [];
$scope.contentDescTypes = [];
call_http_get($http, './ajax/dsm/browsableFields' , function(res) {
$scope.protocols = res.data.protocols;
$scope.compatibilityLevels = res.data.compatibilityLevels;
$scope.contentDescTypes = res.data.contentDescTypes;
});
$scope.save = function() {
var record = angular.copy($scope.api);
record.id = $scope.prefix + record.id;
record.datasource = $routeParams.dsId;
json_http_post($http, './ajax/dsm/api', record, function(res) {
$scope.api = res.data;
});
}
});

View File

@ -0,0 +1,72 @@
// Spinner show/hide methods ~ Andrea Mannocci
var spinnerOpts = {
lines: 15,
length: 16,
width: 5,
radius: 25,
color: '#eeeeee',
className: 'spinner',
top: '40%'
};
var spinnerTarget = document.getElementById('spinnerdiv');
var spinner;
function showSpinner() {
spinner = new Spinner(spinnerOpts).spin(spinnerTarget);
spinnerTarget.style.visibility = 'visible';
}
function hideSpinner() {
spinnerTarget.style.visibility = 'hidden';
spinner.stop();
}
function call_http_get($http, url, onSuccess) {
showSpinner();
$http.get(url).then(function successCallback(res) {
hideSpinner();
onSuccess(res);
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function call_http_delete($http, url, onSuccess) {
showSpinner();
$http.delete(url).then(function successCallback(res) {
hideSpinner();
onSuccess(res);
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function json_http_post($http, url, obj, onSuccess) {
showSpinner();
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
$http.post(url, obj).then(function successCallback(res) {
hideSpinner();
onSuccess(res);
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function params_http_post($http, url, params, onSuccess) {
showSpinner();
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
$http.post(url, params).then(function successCallback(res) {
hideSpinner();
onSuccess(res);
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}

View File

@ -1,78 +0,0 @@
var app = angular.module('resourcesApp', []);
app.controller('resourcesController', function($scope, $http) {
$scope.resources = [];
$scope.tmpRes = {};
$scope.tmpContent = "loading...";
$scope.type = typeId();
$scope.reload = function() {
call_http_get($http, './ajax/resources/' + encodeURIComponent($scope.type) + '?' + $.now(), function(res) {
$scope.resources = res.data;
});
};
$scope.prepareNewResource = function() {
$scope.tmpRes = {
'name' : '',
'description' : '',
'content' : ''
};
}
$scope.prepareEditMetadata = function(r) {
$scope.tmpRes = angular.copy(r);
}
$scope.prepareEditContent = function(r) {
$scope.tmpRes = angular.copy(r);
$scope.tmpContent = "loading...";
call_http_get($http, './ajax/resources/' + encodeURIComponent(r.id) + '/content?' + $.now(), function(res) {
if (res.data instanceof Object) {
$scope.tmpContent = JSON.stringify(res.data, null, "\t");
} else {
$scope.tmpContent = res.data;
}
});
}
$scope.createNewResource = function(r) {
params_http_post($http, './ajax/resources/?' + $.now(), $.param({
'name' : r.name,
'type' : $scope.type,
'description' : r.description,
'content' : r.content
}), function(res) {
alert("Resource saved");
$('#newResourceModal').modal('hide');
$scope.reload();
});
}
$scope.saveMetadata = function(id, md) {
json_http_post($http, './ajax/resources/' + encodeURIComponent(id) + '/metadata?' + $.now(), md, function(res) {
alert("Resource saved");
$scope.reload();
});
};
$scope.saveContent = function(id, content) {
params_http_post($http, './ajax/resources/' + encodeURIComponent(id) + '/content?' + $.now(), $.param({
'content' : content
}), function(res) {
alert("Resource saved");
$('#editContentModal').modal('hide');
});
};
$scope.deleteResource = function(r) {
if (confirm("Are you sure ?")) {
call_http_delete($http, './ajax/resources/' + encodeURIComponent(r.id) + '?' + $.now(), function(res) {
alert("Resource deleted");
$scope.reload();
});
}
};
$scope.reload();
});

View File

@ -1,134 +0,0 @@
var app = angular.module('vocabulariesApp', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/list', { templateUrl: 'vocs/list.html', controller: 'vocListController' })
.when('/edit', { templateUrl: 'vocs/editor.html', controller: 'vocEditorController' })
.otherwise({ redirectTo: '/list' });
}
]);
app.controller('vocListController', function($scope, $http, $location) {
$scope.vocabularies = [];
$scope.tmpVoc = {};
$scope.mode = '';
call_http_get($http, './ajax/vocs/?' + $.now(), function(res) {
$scope.vocabularies = res.data;
});
$scope.prepareNewVoc = function() {
$scope.mode = 'new';
$scope.tmpVoc = {
'id' : '',
'name' : '',
'description' : ''
};
}
$scope.prepareEditVoc = function(voc) {
$scope.mode = 'edit';
$scope.tmpVoc = angular.copy(voc);
}
$scope.saveVocabulary = function(voc) {
if ($scope.mode == 'new') {
var found = false;
angular.forEach($scope.vocabularies, function(v) {
if (voc.id == v.id) { found = true; };
});
if (found) {
alert("Insertion failed: vocabulary already exists !");
return;
}
}
json_http_post($http, './ajax/vocs/?' + $.now(), voc, function(res) {
$scope.vocabularies = res.data;
alert("Vocabulary saved");
});
};
$scope.deleteVocabulary = function(id) {
if (confirm("Are you sure ?")) {
call_http_delete($http, './ajax/vocs/' + encodeURIComponent(id) + '?' + $.now(), function(res) {
$scope.vocabularies = res.data;
alert("Vocabulary deleted");
});
}
};
});
app.controller('vocEditorController', function($scope, $http, $location, $routeParams) {
$scope.terms = [];
$scope.vocId = $routeParams.id;
$scope.vocInfo = {};
$scope.editTermCode = '';
$scope.tmpTerm = {};
$scope.mode = '';
$scope.currTerm = [];
$scope.baseUrl = './ajax/vocs/' + encodeURIComponent($scope.vocId);
call_http_get($http, $scope.baseUrl + '?' + $.now(), function(res) {
$scope.vocInfo = res.data;
call_http_get($http, $scope.baseUrl + '/terms?' + $.now(), function(res) {
$scope.terms = res.data;
});
});
$scope.setCurrTerm = function(term) {
$scope.currTerm = angular.copy(term);
}
$scope.prepareNewTerm = function() {
$scope.mode = 'new';
$scope.editTermCode = '';
$scope.tmpTerm = {
'code' : '',
'name' : '',
'encoding' : 'OPENAIRE',
'synonyms' : []
};
}
$scope.prepareEditTerm = function(term) {
$scope.mode = 'edit';
$scope.editTermCode = term.code;
$scope.tmpTerm = angular.copy(term);
}
$scope.saveTerm = function(term) {
var url = $scope.baseUrl + '/terms?' + $.now();
json_http_post($http, url, term, function(res) {
if ($scope.editTermCode != '' && $scope.editTermCode != $scope.tmpTerm.code) {
var deleteUrl = $scope.baseUrl + '/terms/' + encodeURIComponent($scope.editTermCode) + '?' + $.now();
call_http_delete($http, deleteUrl, function(res) {
$scope.terms = res.data;
alert("Term replaced");
});
} else {
$scope.terms = res.data;
alert("Term saved");
}
});
};
$scope.deleteTerm = function(code) {
if (confirm("Are you sure ?")) {
var url = $scope.baseUrl + '/terms/' + encodeURIComponent(code) + '?' + $.now();
call_http_delete($http, url, function(res) {
$scope.terms = res.data;
alert("Term deleted");
});
}
};
});

View File

@ -1,93 +0,0 @@
var app = angular.module('wfHistoryApp', []);
app.controller('wfHistoryController', function($scope, $http) {
$scope.fromDate = fromDate();
$scope.toDate = toDate();
$scope.maxNumberOfRecentWfs = maxNumberOfRecentWfs();
$scope.workflows = [];
$scope.currentWf = {};
$scope.currDetailsKey = '';
$scope.currDetailsValue = '';
$scope.sortField = 'processId';
$scope.sortReverse = false;
$scope.reload = function() {
var url = './ajax/wfs/?' + $.now();
if ($scope.fromDate > 0) { url += "&from=" + $scope.fromDate; }
if ($scope.toDate > 0) { url += "&to=" + $scope.toDate; }
call_http_get($http, url, function(res) {
$scope.workflows = res.data;
});
};
$scope.setCurrentWf = function(wf) {
$scope.currentWf = angular.copy(wf);
$scope.currDetailsKey = '';
$scope.currDetailsValue = '';
$scope.currentWf.arrayDetails = [];
$scope.currentWf.duration = $scope.calculateDateDiff(parseInt(wf.details['system:startDate']), parseInt(wf.details['system:endDate']));
angular.forEach(wf.details, function(v,k) {
$scope.currentWf.arrayDetails.push({'k':k, 'v':v});
});
}
$scope.setCurrentDetailParam = function(k, v) {
$scope.currDetailsKey = k;
$scope.currDetailsValue = v;
}
$scope.calculateDateDiff = function(start, end) {
if (start <= 0 || end <= 0) {
return '-';
}
var seconds = 0;
var minutes = 0;
var hours = 0;
var days = 0;
if (end > start) {
seconds = Math.round((end - start) / 1000);
if (seconds > 60) {
minutes = Math.floor(seconds / 60);
seconds = seconds % 60;
if (minutes > 60) {
hours = Math.floor(minutes / 60);
minutes = minutes % 60;
if (hours > 24) {
days = Math.floor(hours / 24);
hours = hours % 24;
}
}
}
}
var res = '';
if (days > 0) {
if (res) { res += ', '; }
res += days + " day(s)"
}
if (hours > 0) {
if (res) { res += ', '; }
res += hours + " hour(s)"
}
if (minutes > 0) {
if (res) { res += ', '; }
res += minutes + " minute(s)"
}
if (seconds > 0) {
if (res) { res += ', '; }
res += seconds + " second(s)"
}
if (!res) {
res = '0 seconds';
}
return res;
}
$scope.reload();
});

View File

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<head th:replace="fragments/mainParts.html :: htmlHeader('Contexts')"></head>
<body ng-app="contextsApp">
<nav th:replace="fragments/mainParts.html :: mainMenu('Contexts')"></nav>
<div ng-view></div>
</body>
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
<script src="js/contexts.js"></script>
</html>

View File

@ -1,137 +0,0 @@
<!DOCTYPE html>
<html>
<head th:replace="fragments/mainParts.html :: htmlHeader('Search Datasources')"></head>
<body ng-app="dsmApp" ng-controller="dsmSearchController">
<nav th:replace="fragments/mainParts.html :: mainMenu('Search Datasources')"></nav>
<div ng-view></div>
</body>
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
<script th:inline="javascript">
/*<![CDATA[*/
function browsableFields() { return /*[[${browsableFields}]]*/ ''; }
function pageSize() { return /*[[${pageSize}]]*/ ''; }
function protocols() { return /*[[${protocols}]]*/ ''; }
function compatibilityLevels() { return /*[[${compatibilityLevels}]]*/ ''; }
function contentDescTypes() { return /*[[${contentDescTypes}]]*/ ''; }
/*]]>*/
</script>
<script>
var app = angular.module('dsmApp', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/search', { templateUrl: 'dsm/search.html', controller: 'dsmSearchController' })
.when('/results/:page/:size', { templateUrl: 'dsm/results.html', controller: 'dsmResultsController' })
.when('/results/:field/:page/:size', { templateUrl: 'dsm/results.html', controller: 'dsmResultsController' })
.when('/add/api', { templateUrl: 'dsm/addApi.html', controller: 'dsmAddApiController' })
.when('/api', { templateUrl: 'dsm/api.html', controller: 'dsmApiController' })
.otherwise({ redirectTo: '/search' });
}
]);
app.controller('dsmSearchController', function($scope, $http, $location, $timeout) {
$scope.browseFieldId = "";
$scope.browseFieldName = "";
$scope.browseData = [];
$scope.browsableFields = browsableFields();
$scope.browseField = function(id, label) {
$scope.browseFieldId = id;
$scope.browseFieldName = name;
$scope.browseData = [];
call_http_get($http, './ajax/dsm/browse/' + encodeURIComponent(id) + '?' + $.now(), function(res) {
$scope.browseData = res.data;
});
}
$scope.search = function(field, value) {
var path = "/results";
if (field) { path += '/' + encodeURIComponent(field); }
path += '/0/' + pageSize();
$timeout(function() {
$location.path(path).search('value', value);
}, 1000);
}
});
app.controller('dsmResultsController', function($scope, $http, $location, $routeParams) {
$scope.field = $routeParams.field;
$scope.value = $routeParams.value;
$scope.pageSize = $routeParams.size;
$scope.currPage = $routeParams.page;
$scope.nResults = 0;
$scope.results = [];
$scope.nPages = 0;
var url = './ajax/dsm/';
if ($scope.field) { url += 'searchByField/' + encodeURIComponent($scope.field); }
else { url += 'search' }
url += '/' + $scope.currPage + '/' + $scope.pageSize;
url += '?value=' + encodeURIComponent($scope.value) + '&' + $.now();
call_http_get($http, url, function(res) {
$scope.results = res.data.content;
$scope.nResults = res.data.totalElements;
$scope.currPage = res.data.number;
$scope.nPages = res.data.totalPages;
});
$scope.gotoPage = function(page) {
$scope.results = [];
var path = "/results";
if ($scope.field) { path += '/' + encodeURIComponent($scope.field); }
path += '/' + page + '/' + $scope.pageSize;
$location.path(path).search('value', $scope.value);
}
});
app.controller('dsmApiController', function($scope, $http, $location, $timeout, $routeParams) {
$scope.apiId = $routeParams.id;
$scope.api = {};
call_http_get($http, './ajax/dsm/api?id=' + encodeURIComponent($scope.apiId) , function(res) {
$scope.api = res.data;
});
});
app.controller('dsmAddApiController', function($scope, $http, $location, $timeout, $routeParams) {
$scope.dsName = $routeParams.dsName;
$scope.prefix = 'api_________::' + $routeParams.dsId + '::';
$scope.api = {}
$scope.protocols = protocols();
$scope.compatibilityLevels = compatibilityLevels();
$scope.contentDescTypes = contentDescTypes();
$scope.save = function() {
var record = angular.copy($scope.api);
record.id = $scope.prefix + record.id;
record.datasource = $routeParams.dsId;
json_http_post($http, './ajax/dsm/api', record, function(res) {
$scope.api = res.data;
});
}
});
</script>
</html>

View File

@ -1,194 +0,0 @@
<!DOCTYPE HTML>
<html>
<head th:fragment="htmlHeader(pageTitle)">
<title th:text="|OpenAIRE Tools - ${pageTitle}|"></title>
<link rel="stylesheet" href="common/css/bootstrap.cerulean.min.css" />
<link rel="stylesheet" href="common/css/fontawesome-all.min.css" />
<style type="text/css">
td,th {
vertical-align: middle !important;
}
label {
font-weight: bold;
}
.overlaydiv {
position: fixed;
width: 100%;
height: 100%;
z-index: 10000;
visibility: hidden;
}
.grayRectangle {
position: absolute;
background-color: black;
opacity:0.6;
top: 30%;
left: 40%;
width: 20%;
height: 20%;
z-index: 100;
border-radius: 15px;
}
</style>
</head>
<body>
<th:block th:fragment="mainMenu(pageTitle)">
<div id="spinnerdiv" class="overlaydiv">
<span class="grayRectangle"><!--The spinner is added on loading here--></span>
</div>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/main">D-Net</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="./main">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Datasources</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="./dsm">Search</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Configuration</a>
<div class="dropdown-menu">
<h6 class="dropdown-header">Simple resources</h6>
<a th:each="t: ${resTypes}"
th:if="${t.simple}"
class="dropdown-item"
th:href="@{'./resources?type=' + ${t.id}}">
<span th:text="${t.name}"></span>
<span th:text="${t.count}" class="badge badge-primary"></span>
</a>
<div class="dropdown-divider"></div>
<h6 class="dropdown-header">Advanced resources</h6>
<a th:each="t: ${resTypes}"
th:unless="${t.simple}"
class="dropdown-item"
th:href="@{'./resources?type=' + ${t.id}}">
<span th:text="${t.name}"></span>
<span th:text="${t.count}" class="badge badge-primary"></span>
</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Logs</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="./wf_history">Workflow history</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Info</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="./info">Container Info</a>
<a class="dropdown-item" href="./docs" target="_blank">API documentation</a>
</div>
</li>
</ul>
</div>
</nav>
<h1 class="ml-3 mb-4 mt-3" th:text="${pageTitle}"></h1>
</th:block>
</body>
<th:block th:fragment="scripts">
<script>
// Spinner show/hide methods ~ Andrea Mannocci
var spinnerOpts = {
lines: 15,
length: 16,
width: 5,
radius: 25,
color: '#eeeeee',
className: 'spinner',
top: '40%'
};
var spinnerTarget = document.getElementById('spinnerdiv');
var spinner;
function showSpinner() {
spinner = new Spinner(spinnerOpts).spin(spinnerTarget);
spinnerTarget.style.visibility = 'visible';
}
function hideSpinner() {
spinnerTarget.style.visibility = 'hidden';
spinner.stop();
}
function call_http_get($http, url, onSuccess) {
showSpinner();
$http.get(url).then(function successCallback(res) {
hideSpinner();
onSuccess(res);
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function call_http_delete($http, url, onSuccess) {
showSpinner();
$http.delete(url).then(function successCallback(res) {
hideSpinner();
onSuccess(res);
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function json_http_post($http, url, obj, onSuccess) {
showSpinner();
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
$http.post(url, obj).then(function successCallback(res) {
hideSpinner();
onSuccess(res);
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function params_http_post($http, url, params, onSuccess) {
showSpinner();
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
$http.post(url, params).then(function successCallback(res) {
hideSpinner();
onSuccess(res);
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
</script>
<script src="common/js/jquery.min.js"></script>
<script src="common/js/popper.min.js"></script>
<script src="common/js/bootstrap.min.js"></script>
<script src="common/js/angular.min.js"></script>
<script src="common/js/angular-route.min.js"></script>
<script src="common/js/spin.js"></script>
</th:block>
</html>

View File

@ -1,85 +0,0 @@
<!DOCTYPE html>
<html>
<head th:replace="fragments/mainParts.html :: htmlHeader('Info')"></head>
<body ng-app="infoApp" ng-controller="infoController">
<nav th:replace="fragments/mainParts.html :: mainMenu('Info')"></nav>
<div class="container-fluid">
<div class="row">
<div class="col">
<p>
<input type="text" class="form-control form-control-sm" ng-model="infoFilter" placeholder="Filter..."/>
</p>
<div class="card mb-3" ng-repeat="section in info" ng-show="(section.data|filter:infoFilter).length > 0">
<div class="card-body">
<h5 class="card-title">{{section.name}}</h5>
<table class="table table-striped table-sm small" style="table-layout: fixed;">
<thead ng-if="section.name == 'Modules'">
<tr>
<th>Group ID</th>
<th>Artifact ID</th>
<th>Version</th>
<th>POM</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="r in section.data|filter:infoFilter" ng-if="section.name != 'Modules'">
<th style="width: 25%;">{{r.k}}</th>
<td style="overflow-x: auto;"><pre style="margin: 0;">{{r.v}}</pre></td>
</tr>
<tr ng-repeat="r in section.data|filter:infoFilter" ng-if="section.name == 'Modules'" ng-class="{'table-warning' : r.files.length > 1}">
<td>{{r.group}}</td>
<td>{{r.name}}</td>
<td class="text-monospace"><span ng-repeat="v in r.versions">{{v}}<br /></span></td>
<td class="text-monospace"><span ng-repeat="f in r.files">{{f}}<br /></span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</body>
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
<script>
var app = angular.module('infoApp', []);
app.controller('infoController', function($scope, $http) {
$scope.info = [];
call_http_get($http, './ajax/info/?' + $.now(), function(res) {
angular.forEach(res.data, function(section) {
if (section.name != 'Modules') {
angular.forEach(section.data, function(r) {
if (r.k.toLowerCase().endsWith('path') || r.k.toLowerCase().endsWith('.dirs')) {
r.v = r.v.replaceAll(':', ':\n');
}
});
}
});
$scope.info = res.data;
});
});
</script>
</html>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head th:replace="fragments/mainParts.html :: htmlHeader('Homepage')"></head>
<body>
<nav th:replace="fragments/mainParts.html :: mainMenu('Homepage')"></nav>
</body>
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
</html>

View File

@ -1,62 +0,0 @@
<!DOCTYPE html>
<html>
<head th:replace="fragments/mainParts.html :: htmlHeader('Harvesting Protocols')"></head>
<body ng-app="protocolsApp" ng-controller="protocolsController">
<nav th:replace="fragments/mainParts.html :: mainMenu('Harvesting Protocols')"></nav>
<div class="container-fluid">
<p><b>NOTE:</b> Use the api to register new protocols</p>
<div class="row">
<div class="col">
<div class="card mt-4" ng-repeat="prot in protocols">
<div class="card-body">
<h5 class="card-title">{{prot.id}}</h5>
<p class="card-text" ng-if="prot.params.length == 0">No parameters</p>
<table class="table table-sm table-striped" ng-if="prot.params.length > 0">
<thead>
<th style="width: 20%">Parameter</th>
<th>Description</th>
<th style="width: 15%" class="text-center">Type</th>
<th style="width: 15%" class="text-center">Required</th>
<th style="width: 15%" class="text-center">Has Sel Function</th>
</thead>
<tbody>
<tr ng-repeat="p in prot.params">
<th>{{p.name}}</th>
<td>{{p.label}}</td>
<td class="text-center">{{p.type}}</td>
<td class="text-center"><i class="fa fa-check" ng-if="!p.optional"></i></td>
<td class="text-center"><i class="fa fa-check" ng-if="p.hasSelFunction"></i></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
<script>
var app = angular.module('protocolsApp', []);
app.controller('protocolsController', function($scope, $http) {
$scope.protocols = [];
call_http_get($http, './ajax/protocols/?' + $.now(), function(res) {
$scope.protocols = res.data;
});
});
</script>
</html>

View File

@ -1,147 +0,0 @@
<!DOCTYPE html>
<html>
<head th:replace="fragments/mainParts.html :: htmlHeader(${type.name})"></head>
<script th:inline="javascript">
/*<![CDATA[*/
function typeId() { return /*[[${type.id}]]*/ ''; }
/*]]>*/
</script>
<body ng-app="resourcesApp" ng-controller="resourcesController">
<nav th:replace="fragments/mainParts.html :: mainMenu(${type.name})"></nav>
<div class="container-fluid">
<div class="row">
<div class="col">
<p>
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#newResourceModal" ng-click="prepareNewResource()">create a new resource</button>
</p>
<p ng-show="resources.length > 0">
<input type="text" class="form-control form-control-sm" ng-model="resFilter" placeholder="Filter..."/>
</p>
<p>
<span class="text-muted"><b>Number of resources:</b> {{(resources | filter:resFilter).length}}</span>
</p>
<div class="card mb-4" ng-repeat="r in resources|filter:resFilter">
<div class="card-body small">
<span class="badge badge-primary float-right" th:text="${type.contentType}"></span>
<h5 class="card-title" title="{{r.id}}">{{r.name}}</h5>
<p class="card-text">{{r.description}}</p>
<p class="text-muted small">
<b>Id:</b> {{r.id}}<br />
<b>Creation date:</b> {{r.creationDate}}<br />
<b>Modification date:</b> {{r.modificationDate}}
</p>
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#editMetadataModal" ng-click="prepareEditMetadata(r)">edit metadata</button>
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#editContentModal" ng-click="prepareEditContent(r)">edit content</button>
<a href="./api/resources/{{r.id}}/content" class="btn btn-sm btn-success" target="_blank">raw content</a>
<button type="button" class="btn btn-sm btn-danger" ng-click="deleteResource(r)">delete</button>
</div>
</div>
</div>
</div>
</div>
<!-- Modals -->
<div class="modal fade" tabindex="-1" id="editMetadataModal">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Edit metadata</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<form>
<div class="modal-body">
<div class="form-group">
<label>ID</label>
<input type="text" readonly class="form-control-plaintext" ng-model="tmpRes.id" />
</div>
<div class="form-group">
<label>Type</label>
<input type="text" readonly class="form-control-plaintext" ng-model="tmpRes.type" />
</div>
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" ng-model="tmpRes.name" />
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" ng-model="tmpRes.description" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-sm btn-primary" data-dismiss="modal" ng-click="saveMetadata(tmpRes.id, tmpRes)" ng-disabled="!tmpRes.id || !tmpRes.name">Submit</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" id="newResourceModal">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">New resource: <span th:text="${type.id}"></span></h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<form>
<div class="modal-body">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" ng-model="tmpRes.name" />
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" ng-model="tmpRes.description"></textarea>
</div>
<div class="form-group">
<label>Content (<span th:text="${type.contentType}"></span>)</label>
<textarea class="form-control" ng-model="tmpRes.content" rows="25"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-sm btn-primary" ng-click="createNewResource(tmpRes)" ng-disabled="!tmpRes.name || !tmpRes.content">Submit</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" id="editContentModal">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Edit content</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<form>
<div class="modal-body">
<div class="form-group">
<label th:text="${type.contentType}"></label>
<textarea class="form-control" ng-model="tmpContent" rows="25"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-sm btn-primary" ng-click="saveContent(tmpRes.id, tmpContent)" ng-disabled="!tmpContent">Submit</button>
</div>
</form>
</div>
</div>
</div>
</body>
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
<script src="js/simpleResources.js"></script>
</html>

View File

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<head th:replace="fragments/mainParts.html :: htmlHeader('Vocabularies')"></head>
<body ng-app="vocabulariesApp">
<nav th:replace="fragments/mainParts.html :: mainMenu('Vocabularies')"></nav>
<div ng-view></div>
</body>
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
<script src="js/vocabularies.js"></script>
</html>

View File

@ -1,153 +0,0 @@
<!DOCTYPE html>
<html>
<head th:replace="fragments/mainParts.html :: htmlHeader('Workflow history')"></head>
<script th:inline="javascript">
/*<![CDATA[*/
function maxNumberOfRecentWfs() { return /*[[${maxNumberOfRecentWfs}]]*/ -1; }
function fromDate() { return /*[[${fromDate}]]*/ -1; }
function toDate() { return /*[[${toDate}]]*/ -1; }
/*]]>*/
</script>
<body ng-app="wfHistoryApp" ng-controller="wfHistoryController">
<nav th:replace="fragments/mainParts.html :: mainMenu('Workflow history')"></nav>
<div class="container-fluid">
<div class="row">
<div class="col">
<p ng-show="workflows.length > 0">
<input type="text" class="form-control form-control-sm" ng-model="wfFilter" placeholder="Filter..."/>
</p>
<p class="text-muted">
<span ng-show="fromDate < 0 && toDate < 0"><b>Recent workflows</b> (max {{maxNumberOfRecentWfs}})</span>
<span ng-show="fromDate >= 0 && toDate >= 0"><b>Workflows from </b>{{fromDate | date:"yyyy-MM-dd HH:mm:ss"}} <b>to</b> {{toDate | date:"yyyy-MM-dd HH:mm:ss"}}</span>
<span ng-show="fromDate >= 0 && toDate < 0"><b>Workflows from </b>{{fromDate | date:"yyyy-MM-dd HH:mm:ss"}} <b>to</b> <i>undefined</i></span>
<span ng-show="fromDate < 0 && toDate >= 0"><b>Workflows from </b><i>undefined</i> <b>to</b> {{toDate | date:"yyyy-MM-dd HH:mm:ss"}}</span>
<br />
<span><b>Count :</b> {{(workflows | filter:wfFilter).length}}</span>
</p>
<table class="table table-sm table-striped small">
<thead>
<tr>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'processId'; sortReverse = !sortReverse">
Process Id
<i ng-show="sortField == 'processId' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'processId' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 20%">
<a href="javascript:void(0)" ng-click="sortField = 'name'; sortReverse = !sortReverse">
Workflow Name
<i ng-show="sortField == 'name' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'name' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'family'; sortReverse = !sortReverse">
Workflow Family
<i ng-show="sortField == 'family' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'family' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 30%">
<a href="javascript:void(0)" ng-click="sortField = 'dsName'; sortReverse = !sortReverse">
Datasource
<i ng-show="sortField == 'dsName' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'dsName' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'status'; sortReverse = !sortReverse">
Status
<i ng-show="sortField == 'status' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'status' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'startDate'; sortReverse = !sortReverse">
Start Date
<i ng-show="sortField == 'startDate' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'startDate' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
<th style="width: 10%">
<a href="javascript:void(0)" ng-click="sortField = 'endDate'; sortReverse = !sortReverse">
End Date
<i ng-show="sortField == 'endDate' && !sortReverse" class="fa fa-angle-down"></i>
<i ng-show="sortField == 'endDate' && sortReverse" class="fa fa-angle-up"></i>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-show="(workflows|filter:wfFilter).length == 0">
<td colspan="7" class="text-muted">no workflows</td>
</tr>
<tr ng-repeat="wf in workflows | orderBy:sortField:sortReverse | filter:wfFilter">
<th><a href="javascript:void(0)" data-toggle="modal" data-target="#showWfDetailsModal" ng-click="setCurrentWf(wf)">{{wf.processId}}</a></th>
<td>{{wf.name}}</td>
<td>{{wf.family}}</td>
<td>{{wf.dsName}}</td>
<td><span class="badge" ng-class="{
'badge-success' : (wf.status == 'success'),
'badge-danger' : (wf.status == 'failure'),
'badge-primary' : (wf.status != 'success') && (wf.status != 'failure')
}">{{wf.status}}</span></td>
<td>{{wf.startDate}}</td>
<td>{{wf.endDate}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Modals -->
<div class="modal fade" tabindex="-1" id="showWfDetailsModal">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Details</h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>
<b>Started at: </b>{{currentWf.startDate}}<br />
<b>Finished at: </b>{{currentWf.endDate}}<br />
<b>Duration: </b>{{currentWf.duration}}<br />
</p>
<input type="text" class="form-control form-control-sm" ng-model="detailsFilter" placeholder="Filter..."/>
<table class="table table-sm table-striped small mt-2" style="table-layout: fixed;">
<tr ng-repeat="p in currentWf.arrayDetails | filter:detailsFilter">
<th style="width: 30%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"><a href="javascript:void(0)" ng-click="setCurrentDetailParam(p.k,p.v)">{{p.k}}</a></th>
<td style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{p.v}}</td>
</tr>
</table>
<div class="card card-body bg-light" ng-show="currDetailsKey">
<b>{{currDetailsKey}}</b><br />
<pre>{{currDetailsValue}}</pre>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</body>
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
<script src="js/wf_history.js"></script>
</html>