Skip to content

Commit

Permalink
#349 - UI refinements - first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
cbellone committed Jan 4, 2018
1 parent 6864a61 commit 2526c22
Show file tree
Hide file tree
Showing 14 changed files with 259 additions and 207 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,77 +23,68 @@
import alfio.scripting.ScriptingService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.Validate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.security.Principal;
import java.util.List;

@RestController
@AllArgsConstructor
@RequestMapping("/admin/api")
public class ScriptController {
@RequestMapping("/admin/api/extensions")
public class ExtensionApiController {

private static final String SAMPLE_JS;

static {
try {
SAMPLE_JS = new String(Files.readAllBytes(new File(ExtensionApiController.class.getResource("/alfio/extension/sample.js").toURI()).toPath()));
} catch (URISyntaxException | IOException e) {
throw new IllegalStateException("cannot read sample file", e);
}
}

private final ScriptingService scriptingService;
private final UserManager userManager;


@RequestMapping(value = "/scripting", method = RequestMethod.GET)
@RequestMapping(value = "", method = RequestMethod.GET)
public List<ScriptSupport> listAll(Principal principal) {
ensureAdmin(principal);
return scriptingService.listAll();
}

/*
function getScriptMetadata() {
return {async:true, events: ['RESERVATION_CONFIRMATION']};
}
function executeScript(scriptEvent) {
log.warn('hello from script with event: ' + scriptEvent);
}
--
function getScriptMetadata() {
return {async:false, events: ['INVOICE_GENERATION']};
}
function executeScript(scriptEvent) {
return {invoiceNumber: '42'};
@RequestMapping(value = "/sample", method = RequestMethod.GET)
public ScriptSupport getSample() {
return new ScriptSupport("/", "", null, true, true, SAMPLE_JS);
}

--
function getScriptMetadata() {
return {async:false, events: ['INVOICE_GENERATION']};
}
function executeScript(scriptEvent) {
var symbol = restTemplate.getForObject('https://api.coinmarketcap.com/v1/ticker/bitcoin/', Java.type('java.util.ArrayList').class)[0].symbol;
return {invoiceNumber: symbol};
}
--
*/
@RequestMapping(value = "/scripting", method = RequestMethod.POST)
@RequestMapping(value = "", method = RequestMethod.POST)
public void createOrUpdate(@RequestBody Script script, Principal principal) {
ensureAdmin(principal);
scriptingService.createOrUpdate(script);
}

@RequestMapping(value = "/{name}", method = RequestMethod.GET)
public ResponseEntity<ScriptSupport> loadSingle(@RequestParam("path") String path, @PathVariable("name") String name, Principal principal) throws UnsupportedEncodingException {
ensureAdmin(principal);
return scriptingService.getSingle(URLDecoder.decode(path, "UTF-8"), name).map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
}


@RequestMapping(value = "/scripting/{path}/{name}", method = RequestMethod.DELETE)
public void delete(@PathVariable("path") String path, @PathVariable("name") String name, Principal principal) {
@RequestMapping(value = "/{name}", method = RequestMethod.DELETE)
public void delete(@RequestParam("path") String path, @PathVariable("name") String name, Principal principal) {
ensureAdmin(principal);
scriptingService.delete(path, name);
}

@RequestMapping(value = "/scripting/{path}/{name}/toggle/{enable}", method = RequestMethod.POST)
@RequestMapping(value = "/{path}/{name}/toggle/{enable}", method = RequestMethod.POST)
public void toggle(@PathVariable("path") String path, @PathVariable("name") String name, @PathVariable("enable") boolean enable, Principal principal) {
ensureAdmin(principal);
scriptingService.toggle(path, name, enable);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/alfio/repository/ScriptRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import ch.digitalfondue.npjt.QueryRepository;

import java.util.List;
import java.util.Optional;
import java.util.Set;

@QueryRepository
Expand Down Expand Up @@ -50,6 +51,9 @@ int insert(@Bind("path") String path,
@Query("select script from script_support where path = :path and name = :name")
String getScript(@Bind("path") String path, @Bind("name") String name);

@Query("select * from script_support where path = :path and name = :name")
Optional<ScriptSupport> getSingle(@Bind("path") String path, @Bind("name") String name);

@Query("delete from script_event where path_fk = :path and name_fk = :name")
int deleteEventsForPath(@Bind("path") String path, @Bind("name") String name);

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/alfio/scripting/ScriptingService.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public String getScript(String path, String name) {
return scriptRepository.getScript(path, name);
}

public Optional<ScriptSupport> getSingle(String path, String name) {
return scriptRepository.getSingle(path, name);
}

public <T> T executeScriptsForEvent(String event, String basePath, Map<String, Object> payload, Class<T> clazz) {
List<ScriptPathNameHash> activePaths = getActiveScriptsForEvent(event, basePath, false);
T res = null;
Expand Down
30 changes: 30 additions & 0 deletions src/main/resources/alfio/extension/sample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* The script metadata object describes whether or not your extension should be invoked asynchronously, and which events it supports
* @returns {{ async: boolean, events: string[] }}
*/
function getScriptMetadata() {
return {
async: false,
events: [
//supported values:
//'RESERVATION_CONFIRMATION', //fired on reservation confirmation. No results expected.
//'TICKET_ASSIGNMENT', //fired on ticket assignment. No results expected.
//'WAITING_QUEUE_SUBSCRIPTION', //fired on waiting queue subscription. No results expected.
'INVOICE_GENERATION' //fired on invoice generation. Returns the invoice model.
]
};
}

/**
* Executes the extension.
* @param scriptEvent
* @returns Object
*/
function executeScript(scriptEvent) {
log.warn('hello from script with event: ' + scriptEvent);
//this sample calls the https://csrng.net/ website and generates a random invoice number
var symbol = restTemplate.getForObject('https://csrng.net/csrng/csrng.php?min=0&max=100', Java.type('java.util.ArrayList').class)[0].random;
return {
invoiceNumber: symbol
};
}
6 changes: 3 additions & 3 deletions src/main/webapp/WEB-INF/templates/admin/index.ms
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@
<script src="{{request.contextPath}}/resources/js/admin/feature/tickets-list/tickets-list.js"></script>
<script src="{{request.contextPath}}/resources/js/admin/feature/label-template/label-template.js"></script>

<script src="{{request.contextPath}}/resources/js/admin/feature/scripting/scripting.js"></script>
<script src="{{request.contextPath}}/resources/js/admin/feature/scripting/add-update/scripting-add-update.js"></script>
<script src="{{request.contextPath}}/resources/js/admin/feature/extension/extension.js"></script>
<script src="{{request.contextPath}}/resources/js/admin/feature/extension/add-update/extension-add-update.js"></script>

<link rel="stylesheet" href="{{request.contextPath}}/resources/js/admin/feature/reservation/create/reservation-create.css" />

Expand Down Expand Up @@ -117,7 +117,7 @@
<li data-ui-sref-active="active"><a data-ui-sref="organizations" ng-click="ctrl.menuCollapsed = true">Organizations</a></li>
<li data-ui-sref-active="active"><a data-ui-sref="users" ng-click="ctrl.menuCollapsed = true">Users</a></li>
<li data-ui-sref-active="active"><a data-ui-sref="configuration.system" ng-click="ctrl.menuCollapsed = true">Configuration</a></li>
<li data-ui-sref-active="active"><a data-ui-sref="scripting" ng-click="ctrl.menuCollapsed = true">Scripting</a></li>
<li data-ui-sref-active="active"><a data-ui-sref="extension.list" ng-click="ctrl.menuCollapsed = true">Extension</a></li>
{{/isOwner}}
</ul>
<ul class="nav navbar-nav visible-sm visible-xs" ng-if="ctrl.eventName">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div class="container" container-fluid-responsive="">

<div class="page-header">
<h3>{{$ctrl.edit ? 'Update' : 'Add'}} Extension</h3>
</div>
<form role="form" name="script" ng-submit="$ctrl.save($ctrl.extension)" data-error-sensitive novalidate data-oncancel="$ctrl.cancel()">
<div class="modal-body">
<div class="form-group" bs-form-error="$ctrl.extension.path">
<label for="script-path">Path</label>
<input class="form-control" type="text" data-grab-focus id="script-path" name="path" data-ng-model="$ctrl.extension.path" required>
<field-error data-form-obj="script" data-field-obj="$ctrl.extension.path"></field-error>
</div>

<div class="form-group" bs-form-error="$ctrl.extension.name">
<label for="script-name">Name</label>
<input class="form-control" type="text" data-grab-focus id="script-name" name="name" data-ng-model="$ctrl.extension.name" required>
<field-error data-form-obj="script" data-field-obj="$ctrl.extension.name"></field-error>
</div>

<div class="form-group" bs-form-error="$ctrl.extension.enabled">
<label for="script-enabled">Enabled</label>
<input type="checkbox" data-grab-focus id="script-enabled" name="enabled" data-ng-model="$ctrl.extension.enabled">
<field-error data-form-obj="script" data-field-obj="$ctrl.extension.enabled"></field-error>
</div>

<div class="form-group" bs-form-error="$ctrl.extension.script">
<div ui-ace="{
mode: 'javascript',
firstLineNumber: 1,
onLoad: $ctrl.initLoadListener()
}"></div>
<field-error data-form-obj="script" data-field-obj="$ctrl.extension.script"></field-error>
</div>
</div>
<control-buttons data-form-obj="script"></control-buttons>
</form>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
(function() {
'use strict';

angular.module('adminApplication').component('extensionAddUpdate', {
controller: ['$http', '$q', '$state', ExtensionAddUpdateCtrl],
templateUrl: '../resources/js/admin/feature/extension/add-update/extension-add-update.html',
bindings: {
dismiss:'&',
close:'&',
toUpdate:'<'
}
});

function ExtensionAddUpdateCtrl($http, $q, $state) {
var ctrl = this;
ctrl.extension = null;
ctrl.extensionLoader = null;

ctrl.$onInit = function() {
ctrl.edit = ctrl.toUpdate;
if(ctrl.toUpdate) {
ctrl.extensionLoader = $http.get('/admin/api/extensions/' + ctrl.toUpdate.name, {params: {path: ctrl.toUpdate.path}});
} else {
ctrl.extensionLoader = $http.get('/admin/api/extensions/sample');
}
ctrl.extensionLoader.then(function(res) {
ctrl.extension = res.data;
});
};


ctrl.save = function(extension) {
$http.post('/admin/api/extensions', {
path: extension.path,
name: extension.name,
enabled: extension.enabled,
script: extension.script
}).then(function() {
$state.go('extension.list');
});
};

ctrl.initLoadListener = function() {
return function(editor) {
var session = editor.getSession();

// Options
session.setUndoManager(new ace.UndoManager());

session.on("change", function(event, editor) {
var newVal = editor.getValue();
var currVal = ctrl.extension.script;
if(newVal !== currVal) {
ctrl.extension.script = newVal;
}
});

ctrl.extensionLoader.then(function() {
editor.setValue(ctrl.extension.script, 0);
editor.clearSelection();
});

}
};
}
})();
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<div class="container" container-fluid-responsive="">
<h1>Extension</h1>
<hr>
<div class="text-right">
<a class="btn btn-success" ui-sref="extension.new"><i class="fa fa-plus"></i> add new</a>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>path</th>
<th>name</th>
<th>enabled</th>
<th width="10%"><span class="sr-only">actions</span></th>
</tr>
</thead>
<tbody>
<tr ng-if="!$ctrl.scripts || $ctrl.scripts.length == 0">
<td colspan="4">no extensions have been found.</td>
</tr>
<tr ng-repeat="ext in $ctrl.scripts">
<td>{{ext.path}}</td>
<td>{{ext.name}}</td>
<td>{{ext.enabled}}</td>
<td class="text-center">
<a class="btn btn-sm btn-default" ui-sref="extension.edit({path: ext.path, name: ext.name})"><i class="fa fa-edit"></i><span class="sr-only">edit</span></a>
<button class="btn btn-sm btn-danger" ng-click="$ctrl.deleteExtension(ext)"><i class="fa fa-trash"></i><span class="sr-only">delete</span></button>
</td>
</tr>
</tbody>
</table>
</div>
34 changes: 34 additions & 0 deletions src/main/webapp/resources/js/admin/feature/extension/extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
(function() {
'use strict';

angular.module('adminApplication').component('extension', {
controller: ['$http', ExtensionCtrl],
templateUrl: '../resources/js/admin/feature/extension/extension.html'
});

function ExtensionCtrl($http) {
var ctrl = this;

this.$onInit = function() {
load();
};

this.deleteExtension = deleteExtension;


function load() {
$http.get('/admin/api/extensions').then(function(res) {
ctrl.scripts = res.data;
})
}

function deleteExtension(extension) {
if(window.confirm('Delete ' + extension.name+'?')) {
$http.delete('/admin/api/extensions/'+ extension.name, {params: {path: extension.path}}).then(function () {
load();
});
}
}
}

})();

0 comments on commit 2526c22

Please sign in to comment.