mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
740 lines
26 KiB
JavaScript
740 lines
26 KiB
JavaScript
(function() {
|
||
// Get application context
|
||
var app = Sammy.apps['#main'];
|
||
var store = app.store;
|
||
|
||
/**
|
||
* Apps
|
||
*
|
||
*/
|
||
|
||
// List installed apps
|
||
app.get('#/apps', function (c) {
|
||
c.api('GET', '/apps?full', {}, function(data) {
|
||
var apps = data['apps'];
|
||
c.arraySortById(apps);
|
||
c.view('app/app_list', {apps: apps});
|
||
});
|
||
});
|
||
|
||
function levelToColor(level) {
|
||
if (level >= 8) {
|
||
return 'best';
|
||
}
|
||
else if (level > 4) {
|
||
return 'success';
|
||
}
|
||
else if (level >= 1) {
|
||
return 'warning';
|
||
}
|
||
else if (isNaN(level)) {
|
||
return 'default';
|
||
} else {
|
||
return 'danger'
|
||
}
|
||
}
|
||
|
||
function stateToColor(state) {
|
||
if (state === "high-quality") {
|
||
return 'best';
|
||
}
|
||
else if (state === "working") {
|
||
return 'success';
|
||
}
|
||
else {
|
||
return 'danger';
|
||
}
|
||
}
|
||
|
||
function maintainedStateToColor(state) {
|
||
if ( state === "request_help" ) {
|
||
return 'info';
|
||
}
|
||
else if ( state === "request_adoption" ) {
|
||
return 'warning';
|
||
}
|
||
else if ( state === "orphaned" ) {
|
||
return 'danger';
|
||
}
|
||
else {
|
||
return 'success';
|
||
}
|
||
}
|
||
|
||
function combineColors(stateColor, levelColor, installable) {
|
||
if (stateColor === "danger" || levelColor === "danger") {
|
||
return 'danger';
|
||
}
|
||
else if (stateColor === "warning" || levelColor === "warning" || levelColor === "default") {
|
||
return 'warning';
|
||
}
|
||
else
|
||
{
|
||
return 'info';
|
||
}
|
||
}
|
||
|
||
function extractMaintainer(manifest) {
|
||
if (manifest.maintainer === undefined)
|
||
{
|
||
if ((manifest.developer !== undefined) && (manifest.developer.name !== undefined))
|
||
{
|
||
return manifest.developer.name;
|
||
}
|
||
else
|
||
{
|
||
return "?";
|
||
}
|
||
}
|
||
else if (Array.isArray(manifest.maintainer))
|
||
{
|
||
maintainers = [];
|
||
manifest.maintainer.forEach(function(maintainer) {
|
||
if (maintainer.name !== undefined)
|
||
{
|
||
maintainers.push(maintainer.name);
|
||
}
|
||
});
|
||
return maintainers.join(', ');
|
||
}
|
||
else if (manifest.maintainer.name !== undefined)
|
||
{
|
||
return manifest.maintainer.name;
|
||
}
|
||
else
|
||
{
|
||
return "?";
|
||
}
|
||
}
|
||
|
||
|
||
// Display catalog home page where users chooses to browse a specific category
|
||
app.get('#/apps/catalog', function (c) {
|
||
c.api('GET', '/appscatalog?full&with_categories', {}, function (data) {
|
||
c.view('app/app_catalog_home', {categories: data["categories"]}, function() {
|
||
// Configure layout / rendering for app-category-cards
|
||
$('#category-selector').isotope({
|
||
itemSelector: '.app-category-card',
|
||
layoutMode: 'fitRows',
|
||
transitionDuration: 200
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
// Display app catalog for a specific category
|
||
app.get('#/apps/catalog/:category', function (c) {
|
||
var category_id = c.params['category'];
|
||
c.api('GET', '/appscatalog?full&with_categories', {}, function (data) {
|
||
var apps = [];
|
||
$.each(data['apps'], function(name, app) {
|
||
|
||
// Ignore not working apps
|
||
if (app.state === 'notworking') { return; }
|
||
|
||
// Ignore apps not in this category
|
||
if ((category_id !== "all") && (app.category !== category_id)) { return; }
|
||
|
||
app.id = app.manifest.id;
|
||
app.level = parseInt(app.level);
|
||
|
||
if (app.high_quality && app.level > 7)
|
||
{
|
||
app.state = "high-quality";
|
||
}
|
||
if ( app.maintained === false )
|
||
{
|
||
app.maintained = "orphaned";
|
||
}
|
||
else if ( app.maintained === true )
|
||
{
|
||
app.maintained = "maintained";
|
||
}
|
||
|
||
app.manifest.maintainer = extractMaintainer(app.manifest);
|
||
var isWorking = (app.state === 'working' || app.state === "high-quality") && app.level > 0;
|
||
|
||
app.installable = (!app.installed || app.manifest.multi_instance)
|
||
app.levelFormatted = isNaN(app.level) ? '?' : app.level;
|
||
|
||
app.levelColor = levelToColor(app.level);
|
||
app.stateColor = stateToColor(app.state);
|
||
app.maintainedColor = maintainedStateToColor(app.maintained);
|
||
app.installColor = combineColors(app.stateColor, app.levelColor);
|
||
|
||
app.updateDate = app.lastUpdate * 1000 || 0;
|
||
app.isSafe = (app.installColor !== 'danger');
|
||
app.isWorking = isWorking ? "isworking" : "notFullyWorking";
|
||
app.isHighQuality = (app.state === "high-quality") ? "isHighQuality" : "";
|
||
app.decentQuality = (app.level > 4)?"decentQuality":"badQuality";
|
||
|
||
apps.push(app);
|
||
});
|
||
|
||
var category = undefined;
|
||
$.each(data['categories'], function(i, this_category) {
|
||
if (this_category.id === category_id) { category = this_category; }
|
||
});
|
||
|
||
if (category_id === "all") {
|
||
category = {title: y18n.t("all_apps"), icon: "search"};
|
||
}
|
||
|
||
// Sort app list
|
||
c.arraySortById(apps);
|
||
|
||
// setup filtering of apps once the view is loaded
|
||
function setupFilterEvents () {
|
||
// Uses plugin isotope to filter apps (we could had ordering to)
|
||
var cardGrid = jQuery('#apps').isotope({
|
||
itemSelector: '.app-card',
|
||
layoutMode: 'fitRows',
|
||
transitionDuration: 200
|
||
});
|
||
|
||
// Default filter is 'decent quality apps'
|
||
cardGrid.isotope({ filter: '.decentQuality' });
|
||
|
||
$(".subtag-selector button").on("click", function() {
|
||
var selector = $(this).parent();
|
||
$("button", selector).removeClass("active");
|
||
$(this).addClass("active");
|
||
cardGrid.isotope({ filter: filterApps });
|
||
});
|
||
|
||
filterApps = function () {
|
||
|
||
// Check text search
|
||
var input = jQuery("#filter-app-cards").val().toLowerCase();
|
||
if (jQuery(this).find('.app-title').text().toLowerCase().indexOf(input) <= -1) return false;
|
||
|
||
// Check subtags
|
||
var subtag = $(".subtag-selector button.active").data("subtag");
|
||
var this_subtags = jQuery(this).data("subtags");
|
||
if ((subtag !== undefined) && (subtag !== "all")) {
|
||
if ((subtag === "others") && (this_subtags !== "")) return false;
|
||
if ((subtag !== "others") && (this_subtags.split(",").indexOf(subtag) <= -1)) return false;
|
||
}
|
||
|
||
// Check quality criteria
|
||
var class_ = jQuery("#dropdownFilter").data("filter");
|
||
if ((class_ !== '*') && (! jQuery(this).hasClass(class_))) return false;
|
||
|
||
return true;
|
||
},
|
||
|
||
jQuery('.dropdownFilter').on('click', function() {
|
||
// change dropdown label
|
||
jQuery('#app-cards-list-filter-text').text(jQuery(this).find('.menu-item').text());
|
||
// change filter attribute
|
||
jQuery('#dropdownFilter').data("filter", jQuery(this).data("filter"));
|
||
// filter !
|
||
cardGrid.isotope({ filter: filterApps });
|
||
});
|
||
|
||
jQuery("#filter-app-cards").on("keyup", function() {
|
||
cardGrid.isotope({ filter: filterApps });
|
||
});
|
||
|
||
$("#install-custom-app a[role='button']").on('click', function() {
|
||
|
||
var url = $("#install-custom-app input[name='url']")[0].value;
|
||
if (url.indexOf("github.com") < 0) {
|
||
return;
|
||
}
|
||
|
||
c.confirm(
|
||
y18n.t('applications'),
|
||
y18n.t('confirm_install_custom_app'),
|
||
function(){
|
||
c.redirect_to('#/apps/install/custom/' + encodeURIComponent(url));
|
||
}
|
||
);
|
||
});
|
||
};
|
||
|
||
// render
|
||
c.view('app/app_catalog_category', {apps: apps, category: category}, setupFilterEvents);
|
||
|
||
});
|
||
});
|
||
|
||
// Get app information
|
||
app.get('#/apps/:app', function (c) {
|
||
c.api('GET', '/apps/'+c.params['app']+'?full', {}, function(data) {
|
||
c.api('GET', '/users/permissions', {}, function(data_permissions) {
|
||
|
||
// Permissions
|
||
data.permissions = data_permissions.permissions[c.params['app']+".main"]["allowed"];
|
||
|
||
// Multilingual description
|
||
data.description = (typeof data.manifest.description[y18n.locale] !== 'undefined') ?
|
||
data.manifest.description[y18n.locale] :
|
||
data.manifest.description['en']
|
||
;
|
||
|
||
// Multi Instance settings
|
||
data.manifest.multi_instance = data.manifest.multi_instance ? y18n.t('yes') : y18n.t('no');
|
||
data.install_time = new Date(data.settings.install_time * 1000);
|
||
|
||
c.view('app/app_info', data, function() {
|
||
|
||
// Button to set the app as default
|
||
$('button[data-action="set-as-default"]').on("click", function() {
|
||
var app = $(this).data("app");
|
||
c.confirm(
|
||
y18n.t('applications'),
|
||
y18n.t('confirm_app_default'),
|
||
function() { c.api('PUT', '/apps/'+app+'/default', {}, function() { c.refresh() }); }
|
||
);
|
||
});
|
||
|
||
// Button to uninstall the app
|
||
$('button[data-action="uninstall"]').on("click", function() {
|
||
var app = $(this).data("app");
|
||
c.confirm(
|
||
y18n.t('applications'),
|
||
y18n.t('confirm_uninstall', [app]),
|
||
function() {
|
||
c.api('DELETE', '/apps/'+ app, {}, function() {
|
||
c.redirect_to('#/apps');
|
||
});
|
||
}
|
||
);
|
||
});
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
//
|
||
// App actions
|
||
//
|
||
|
||
// Get app actions list
|
||
app.get('#/apps/:app/actions', function (c) {
|
||
c.api('GET', '/apps/'+c.params['app']+'/actions', {}, function(data) {
|
||
$.each(data.actions, function(_, action) {
|
||
formatYunoHostStyleArguments(action.arguments, c.params);
|
||
|
||
// Multilingual description
|
||
if (action.description && Array.isArray(action.description))
|
||
action.description = (typeof action.description[y18n.locale] !== 'undefined') ?
|
||
action.description[y18n.locale] :
|
||
action.description['en']
|
||
;
|
||
|
||
});
|
||
|
||
c.view('app/app_actions', data);
|
||
return;
|
||
});
|
||
});
|
||
|
||
// Perform app action
|
||
app.put('#/apps/:app/actions/:action', function(c) {
|
||
// taken from app install
|
||
$.each(c.params, function(k, v) {
|
||
if (typeof(v) === 'object' && Array.isArray(v)) {
|
||
// And return only first value
|
||
c.params[k] = v[0];
|
||
}
|
||
});
|
||
|
||
var app_id = c.params['app'];
|
||
delete c.params['app'];
|
||
var action_id = c.params['action'];
|
||
delete c.params['action'];
|
||
|
||
var params = {
|
||
'args': c.serialize(c.params.toHash())
|
||
}
|
||
|
||
c.api('PUT', '/apps/'+app_id+'/actions/'+action_id, params, function() {
|
||
c.redirect_to('#/apps/'+app_id+'/actions', {slide:false});
|
||
});
|
||
});
|
||
|
||
//
|
||
// App config panel
|
||
//
|
||
|
||
// Get app config panel
|
||
app.get('#/apps/:app/config-panel', function (c) {
|
||
c.api('GET', '/apps/'+c.params['app']+'/config-panel', {}, function(data) {
|
||
$.each(data.config_panel.panel, function(_, panel) {
|
||
$.each(panel.sections, function(_, section) {
|
||
formatYunoHostStyleArguments(section.options, c.params);
|
||
});
|
||
});
|
||
c.view('app/app_config-panel', data);
|
||
});
|
||
});
|
||
|
||
app.post('#/apps/:app/config', function(c) {
|
||
// taken from app install
|
||
$.each(c.params, function(k, v) {
|
||
if (typeof(v) === 'object' && Array.isArray(v)) {
|
||
// And return only first value
|
||
c.params[k] = v[0];
|
||
}
|
||
});
|
||
|
||
var app_id = c.params['app'];
|
||
delete c.params['app'];
|
||
|
||
var params = {
|
||
'args': c.serialize(c.params.toHash())
|
||
}
|
||
|
||
c.api('POST', '/apps/'+app_id+'/config', params, function() {
|
||
c.redirect_to('#/apps/'+app_id+'/config-panel', {slide:false});
|
||
});
|
||
})
|
||
|
||
// Helper function that formats YunoHost style arguments for generating a form
|
||
function formatYunoHostStyleArguments(args, params) {
|
||
if (!args) {
|
||
return;
|
||
}
|
||
|
||
// this is in place modification, I don't like it but it was done this way
|
||
$.each(args, function(k, v) {
|
||
|
||
// Default values
|
||
args[k].type = (typeof v.type !== 'undefined') ? v.type : 'string';
|
||
args[k].inputType = 'text';
|
||
args[k].isPassword = false;
|
||
args[k].isDisplayText = false;
|
||
args[k].required = (typeof v.optional !== 'undefined' && (v.optional == "true" || v.optional == true)) ? '' : 'required';
|
||
args[k].attributes = "";
|
||
args[k].helpText = "";
|
||
args[k].helpLink = "";
|
||
|
||
// Multilingual label
|
||
if (typeof args[k].ask === "string")
|
||
{
|
||
args[k].label = args[k].ask;
|
||
}
|
||
else if (typeof args[k].ask[y18n.locale] !== 'undefined') {
|
||
args[k].label = args[k].ask[y18n.locale];
|
||
}
|
||
else {
|
||
args[k].label = args[k].ask['en'];
|
||
}
|
||
|
||
// Multilingual help text
|
||
if (typeof args[k].help !== 'undefined') {
|
||
args[k].helpText = (typeof args[k].help[y18n.locale] !== 'undefined') ?
|
||
args[k].help[y18n.locale] :
|
||
args[k].help['en']
|
||
;
|
||
}
|
||
|
||
// Input with choices becomes select list
|
||
if (typeof args[k].choices !== 'undefined') {
|
||
// Update choices values with key and checked data
|
||
var choices = []
|
||
$.each(args[k].choices, function(ck, cv){
|
||
// Non key/value choices have numeric key, that we don't want.
|
||
if (typeof ck == "number") {
|
||
// Key is Value in this case.
|
||
ck = cv;
|
||
}
|
||
|
||
choices.push({
|
||
value: ck,
|
||
label: cv,
|
||
selected: (ck == args[k].default) ? true : false,
|
||
});
|
||
});
|
||
args[k].choices = choices;
|
||
}
|
||
|
||
// Special case for domain input.
|
||
// Display a list of available domains
|
||
if (v.name == 'domain' || args[k].type == 'domain') {
|
||
args[k].choices = [];
|
||
$.each(params.domains, function(key, domain){
|
||
args[k].choices.push({
|
||
value: domain,
|
||
label: domain,
|
||
selected: false
|
||
});
|
||
});
|
||
|
||
// Custom help link
|
||
args[k].helpLink += "<a href='#/domains'>"+y18n.t('manage_domains')+"</a>";
|
||
}
|
||
|
||
// Special case for admin / user input.
|
||
// Display a list of available users
|
||
if (v.name == 'admin' || args[k].type == 'user') {
|
||
args[k].choices = [];
|
||
$.each(params.users, function(username, user){
|
||
args[k].choices.push({
|
||
value: username,
|
||
label: user.fullname+' ('+user.mail+')',
|
||
selected: false
|
||
});
|
||
});
|
||
|
||
// Custom help link
|
||
args[k].helpLink += "<a href='#/users'>"+y18n.t('manage_users')+"</a>";
|
||
}
|
||
|
||
// 'app' type input display a list of available apps
|
||
if (args[k].type == 'app') {
|
||
args[k].choices = [];
|
||
$.each(params.apps, function(key, app){
|
||
args[k].choices.push({
|
||
value: app.id,
|
||
label: app.name,
|
||
selected: false
|
||
});
|
||
});
|
||
|
||
// Custom help link
|
||
args[k].helpLink += "<a href='#/apps'>"+y18n.t('manage_apps')+"</a>";
|
||
}
|
||
|
||
// Boolean fields
|
||
if (args[k].type == 'boolean') {
|
||
args[k].inputType = 'checkbox';
|
||
|
||
// Checked or not ?
|
||
if (typeof args[k].default !== 'undefined') {
|
||
if (args[k].default == true) {
|
||
args[k].attributes = 'checked="checked"';
|
||
}
|
||
}
|
||
|
||
// 'default' is used as value, so we need to force it for checkboxes.
|
||
args[k].default = 1;
|
||
|
||
// Checkbox should not be required to be unchecked
|
||
args[k].required = '';
|
||
|
||
// Clone a hidden input with empty value
|
||
// https://stackoverflow.com/questions/476426/submit-an-html-form-with-empty-checkboxes
|
||
var inputClone = {
|
||
name : args[k].name,
|
||
inputType : 'hidden',
|
||
default : 0
|
||
};
|
||
args.push(inputClone);
|
||
}
|
||
|
||
// 'password' type input.
|
||
if (v.name == 'password' || args[k].type == 'password') {
|
||
// Change html input type
|
||
args[k].inputType = 'password';
|
||
args[k].isPassword = true;
|
||
}
|
||
|
||
if (args[k].type == "display_text") {
|
||
args[k].isDisplayText = true;
|
||
args[k].label = args[k].label.split("\n");
|
||
}
|
||
|
||
});
|
||
}
|
||
|
||
// Helper function that build app installation form
|
||
app.helper('appInstallForm', function(appId, manifest, params) {
|
||
var data = {
|
||
id: appId,
|
||
manifest: manifest,
|
||
displayLicense: (manifest['license'] !== undefined && manifest['license'] !== 'free')
|
||
};
|
||
|
||
formatYunoHostStyleArguments(manifest.arguments.install, params);
|
||
|
||
// Multilingual description
|
||
if (typeof manifest.description === 'string')
|
||
{
|
||
data.description = manifest.description;
|
||
}
|
||
else if (typeof manifest.description[y18n.locale] !== 'undefined')
|
||
{
|
||
data.description = manifest.description[y18n.locale];
|
||
}
|
||
else
|
||
{
|
||
data.description = manifest.description['en'];
|
||
}
|
||
|
||
// Multi Instance settings boolean to text
|
||
data.manifest.multi_instance = manifest.multi_instance ? y18n.t('yes') : y18n.t('no');
|
||
|
||
// View app install form
|
||
c.view('app/app_install', data);
|
||
return;
|
||
});
|
||
|
||
// App installation form
|
||
app.get('#/apps/install/:app', function (c) {
|
||
c.api('GET', '/appscatalog?full', {}, function(data) {
|
||
var app_name = c.params["app"];
|
||
var app_infos = data["apps"][app_name];
|
||
if (app_infos['state'] === "validated")
|
||
{
|
||
app_infos['state'] = "official";
|
||
}
|
||
var state_color = stateToColor(app_infos['state']);
|
||
var level_color = levelToColor(parseInt(app_infos['level']));
|
||
var is_safe_for_install_color = combineColors(state_color, level_color);
|
||
|
||
if ((is_safe_for_install_color === "warning") || (is_safe_for_install_color === "danger"))
|
||
{
|
||
c.confirm(
|
||
y18n.t("applications"),
|
||
y18n.t("confirm_install_app_"+is_safe_for_install_color),
|
||
function(){
|
||
c.appInstallForm(
|
||
app_name,
|
||
app_infos.manifest,
|
||
c.params
|
||
);
|
||
},
|
||
function () {
|
||
c.redirect_to('#/apps/catalog');
|
||
}
|
||
);
|
||
}
|
||
else
|
||
{
|
||
c.appInstallForm(
|
||
app_name,
|
||
app_infos.manifest,
|
||
c.params
|
||
);
|
||
}
|
||
});
|
||
});
|
||
|
||
// Install app (POST)
|
||
app.post('#/apps', function(c) {
|
||
// Warn admin if app is going to be installed on domain root.
|
||
if (c.params['path'] !== '/' || confirm(y18n.t('confirm_install_domain_root', [c.params['domain']]))) {
|
||
var params = {
|
||
label: c.params['label'],
|
||
app: c.params['app']
|
||
};
|
||
|
||
// Check for duplicate arg produced by empty checkbox. (See inputClone)
|
||
delete c.params['label'];
|
||
delete c.params['app'];
|
||
$.each(c.params, function(k, v) {
|
||
if (typeof(v) === 'object' && Array.isArray(v)) {
|
||
// And return only first value
|
||
c.params[k] = v[0];
|
||
}
|
||
});
|
||
|
||
params['args'] = c.serialize(c.params.toHash());
|
||
|
||
// Do not pass empty args.
|
||
if (params['args'] === "") {
|
||
delete params['args'];
|
||
}
|
||
|
||
c.api('POST', '/apps', params, function() {
|
||
c.redirect_to('#/apps');
|
||
});
|
||
}
|
||
else {
|
||
c.flash('warning', y18n.t('app_install_cancel'));
|
||
c.refresh();
|
||
}
|
||
});
|
||
|
||
// Install custom app from github
|
||
app.get('#/apps/install/custom/:url', function(c) {
|
||
|
||
// Force trailing slash
|
||
url = c.params['url'];
|
||
url = url.replace(/\/?$/, '/');
|
||
raw_manifest_url = url.replace('github.com', 'raw.githubusercontent.com') + 'master/manifest.json'
|
||
|
||
// Fetch manifest.json
|
||
jQuery.ajax({ url: raw_manifest_url, type: 'GET' })
|
||
.done(function(manifest) {
|
||
// raw.githubusercontent.com serve content as plain text
|
||
manifest = jQuery.parseJSON(manifest) || {};
|
||
|
||
c.appInstallForm(
|
||
url,
|
||
manifest,
|
||
c.params
|
||
);
|
||
|
||
})
|
||
.fail(function(xhr) {
|
||
c.flash('fail', y18n.t('app_install_custom_no_manifest'));
|
||
c.redirect("#/apps/catalog/");
|
||
});
|
||
|
||
});
|
||
|
||
// Get app change label page
|
||
app.get('#/apps/:app/changelabel', function (c) {
|
||
c.api('GET', '/apps/'+c.params['app']+'?full', {}, function(app_data) {
|
||
data = {
|
||
id: c.params['app'],
|
||
label: app_data.settings.label,
|
||
};
|
||
c.view('app/app_changelabel', data);
|
||
});
|
||
});
|
||
|
||
// Change app label
|
||
app.post('#/apps/:app/changelabel', function (c) {
|
||
params = {'new_label': c.params['label']};
|
||
c.api('PUT', '/apps/' + c.params['app'] + '/label', params, function(data) {
|
||
c.redirect_to('#/apps/'+ c.params['app']);
|
||
});
|
||
});
|
||
|
||
// Get app change URL page
|
||
app.get('#/apps/:app/changeurl', function (c) {
|
||
c.api('GET', '/apps/'+c.params['app']+'?full', {}, function(app_data) {
|
||
c.api('GET', '/domains', {}, function(domain_data) {
|
||
|
||
// Display a list of available domains
|
||
var domains = [];
|
||
$.each(domain_data.domains, function(k, domain) {
|
||
domains.push({
|
||
value: domain,
|
||
label: domain,
|
||
// Select current domain
|
||
selected: (domain == app_data.settings.domain ? true : false)
|
||
});
|
||
});
|
||
|
||
data = {
|
||
id: c.params['app'],
|
||
label: app_data.manifest.name,
|
||
domains: domains,
|
||
// Pre-fill with current path
|
||
path: app_data.settings.path
|
||
};
|
||
c.view('app/app_changeurl', data);
|
||
});
|
||
});
|
||
});
|
||
|
||
// Change app URL
|
||
app.post('#/apps/:app/changeurl', function (c) {
|
||
c.confirm(
|
||
y18n.t('applications'),
|
||
y18n.t('confirm_app_change_url', [c.params['app']]),
|
||
function() {
|
||
params = {'domain': c.params['domain'], 'path': c.params['path']};
|
||
c.api('PUT', '/apps/' + c.params['app'] + '/changeurl', params, function(data) {
|
||
c.redirect_to('#/apps/'+ c.params['app']);
|
||
});
|
||
}
|
||
);
|
||
});
|
||
})();
|