diff --git a/src/css/style.less b/src/css/style.less index 21b27fa8..3c8322c2 100644 --- a/src/css/style.less +++ b/src/css/style.less @@ -101,10 +101,6 @@ button { color: transparent; } -.label { - border-radius: 1px; -} - /* * The top heading of the doc * @@ -706,12 +702,15 @@ input[type='radio'].nice-radio { #view-groups { .panel-heading a { text-decoration: none; - &.group-delete { - float:right; - color:lighten(@label-danger-bg, 20%); - :hover { - color:@label-danger-bg; - } + } + .group-delete { + font-size: 24px; + line-height: 24px; + padding: 0; + float:right; + color:lighten(@label-danger-bg, 20%); + :hover { + color:@label-danger-bg; } } .panel-body { @@ -725,6 +724,10 @@ input[type='radio'].nice-radio { .dropdown-menu { max-height: 200px; overflow-y: auto; + + button { + background: none; + } } .label-removable { // The following match properties from regular btn's @@ -743,15 +746,17 @@ input[type='radio'].nice-radio { margin-right:7px; // Spacing between labels - > a { + > button { + line-height: 12px; margin-left:6px; + padding: 0; padding-left:6px; border-left: #ccc 1px solid; color:lighten(@label-info-bg,20); text-decoration: none; } - > a:hover { + > button:hover { color:@label-info-bg; } } @@ -925,3 +930,37 @@ input[type='radio'].nice-radio { float: none !important; } } + +.notransition { + -webkit-transition: none !important; + -moz-transition: none !important; + -o-transition: none !important; + transition: none !important; +} + +/* Just to be able to override list-group-item with alert-success's background and border colors */ +.alert-success-yo { + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-warning-yo { + background-color: #fcf8e3; + border-color: #faebcc; +} + +.alert-danger-yo { + background-color: #f2dede; + border-color: #ebccd1; +} + +.alert-info-yo { + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-ignored-yo { + background-color: ghostwhite; + border-color: lightgrey; + color: grey; +} diff --git a/src/index.html b/src/index.html index 377f1727..c693f55a 100644 --- a/src/index.html +++ b/src/index.html @@ -81,8 +81,8 @@
diff --git a/src/js/yunohost/controllers/apps.js b/src/js/yunohost/controllers/apps.js index 3bff1be4..17c91e10 100644 --- a/src/js/yunohost/controllers/apps.js +++ b/src/js/yunohost/controllers/apps.js @@ -10,7 +10,7 @@ // List installed apps app.get('#/apps', function (c) { - c.api('/apps?installed', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 + c.api('GET', '/apps?installed', {}, function(data) { var apps = data['apps']; c.arraySortById(apps); c.view('app/app_list', {apps: apps}); @@ -109,8 +109,8 @@ // List available apps app.get('#/apps/install', function (c) { - c.api('/apps', function (data) { // http://api.yunohost.org/#!/app/app_list_get_8 - c.api('/apps?raw', function (dataraw) { // http://api.yunohost.org/#!/app/app_list_get_8 + c.api('GET', '/apps', {}, function (data) { + c.api('GET', '/apps?raw', {}, function (dataraw) { var apps = [] $.each(data['apps'], function(k, v) { app = dataraw[v['id']]; @@ -201,37 +201,59 @@ // Get app information app.get('#/apps/:app', function (c) { - c.api('/apps/'+c.params['app']+'?raw', function(data) { // http://api.yunohost.org/#!/app/app_info_get_9 - c.api('/users/permissions', function(data_permissions) { + c.api('GET', '/apps/'+c.params['app']+'?raw', {}, function(data) { + c.api('GET', '/users/permissions', {}, function(data_permissions) { - // Permissions - data.permissions = data_permissions.permissions[c.params['app']+".main"]["allowed"]; + // 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'] - ; + // 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); + // 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); + 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'); + }); + } + ); + }); }); }); - }); - - // Get app debug page - app.get('#/apps/:app/debug', function (c) { - c.api('/apps/'+c.params['app']+'/debug', function(data) { - c.view('app/app_debug', data); }); }); + // + // App actions + // + // Get app actions list app.get('#/apps/:app/actions', function (c) { - c.api('/apps/'+c.params['app']+'/actions', function(data) { + c.api('GET', '/apps/'+c.params['app']+'/actions', {}, function(data) { $.each(data.actions, function(_, action) { formatYunoHostStyleArguments(action.arguments, c.params); @@ -248,7 +270,7 @@ }); }); - // Perform application + // Perform app action app.put('#/apps/:app/actions/:action', function(c) { // taken from app install $.each(c.params, function(k, v) { @@ -267,14 +289,18 @@ 'args': c.serialize(c.params.toHash()) } - c.api('/apps/'+app_id+'/actions/'+action_id, function() { // http://api.yunohost.org/#!/app/app_install_post_2 - c.redirect('#/apps/'+app_id+'/actions'); - }, 'PUT', params); + 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('/apps/'+c.params['app']+'/config-panel', function(data) { + 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); @@ -300,16 +326,16 @@ 'args': c.serialize(c.params.toHash()) } - c.api('/apps/'+app_id+'/config', function() { // http://api.yunohost.org/#!/app/app_install_post_2 - c.redirect('#/apps/'+app_id+'/config-panel'); - }, 'POST', params); + c.api('POST', '/apps/'+app_id+'/config', params, function() { + c.redirect_to('#/apps/'+app_id+'/config-panel', {slide:false}); + }); }) // Special case for custom app installation. app.get('#/apps/install/custom', function (c) { // If we try to GET /apps/install/custom, it means that installation fail. // Need to redirect to apps/install to get rid of pacamn and see the log. - c.redirect('#/apps/install'); + c.redirect_to('#/apps/install'); }); // Helper function that formats YunoHost style arguments for generating a form @@ -480,7 +506,7 @@ // App installation form app.get('#/apps/install/:app', function (c) { - c.api('/apps?raw', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 + c.api('GET', '/apps?raw', {}, function(data) { var app_name = c.params["app"]; var app_infos = data[app_name]; if (app_infos['state'] === "validated") @@ -502,10 +528,6 @@ app_infos.manifest, c.params ); - }, - function(){ - $('div.loader').remove(); - c.redirect('#/apps/install'); } ); } @@ -546,14 +568,13 @@ delete params['args']; } - c.api('/apps', function() { // http://api.yunohost.org/#!/app/app_install_post_2 - c.redirect('#/apps'); - }, 'POST', params); + c.api('POST', '/apps', params, function() { + c.redirect_to('#/apps'); + }); } else { c.flash('warning', y18n.t('app_install_cancel')); - store.clear('slide'); - c.redirect('#/apps/install'); + c.refresh(); } }); @@ -593,77 +614,35 @@ }) .fail(function(xhr) { c.flash('fail', y18n.t('app_install_custom_no_manifest')); - store.clear('slide'); - c.redirect('#/apps/install'); + c.refresh(); }); - }, - function(){ - c.flash('warning', y18n.t('app_install_cancel')); - store.clear('slide'); - c.redirect('#/apps/install'); - } - ); - }); - - // Remove installed app - app.get('#/apps/:app/uninstall', function (c) { - c.confirm( - y18n.t('applications'), - y18n.t('confirm_uninstall', [c.params['app']]), - function() { - c.api('/apps/'+ c.params['app'], function() { // http://api.yunohost.org/#!/app/app_remove_delete_4 - c.redirect('#/apps'); - }, 'DELETE'); - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']); - } - ); - }); - - // Make app default - app.get('#/apps/:app/default', function (c) { - c.confirm( - y18n.t('applications'), - y18n.t('confirm_app_default'), - function() { - c.api('/apps/'+ c.params['app'] +'/default', function() { // - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']); - }, 'PUT'); - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']); } ); }); // Get app change label page app.get('#/apps/:app/changelabel', function (c) { - c.api('/apps/'+c.params['app']+'?raw', function(app_data) { - data = { + c.api('GET', '/apps/'+c.params['app']+'?raw', {}, function(app_data) { + data = { id: c.params['app'], label: app_data.settings.label, - }; - c.view('app/app_changelabel', data); + }; + c.view('app/app_changelabel', data); }); }); // Change app label app.post('#/apps/:app/changelabel', function (c) { params = {'new_label': c.params['label']}; - c.api('/apps/' + c.params['app'] + '/label', function(data) { // Call changelabel API - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']); - }, 'PUT', params); + 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('/apps/'+c.params['app']+'?raw', function(app_data) { - c.api('/domains', function(domain_data) { // http://api.yunohost.org/#!/domain/domain_list_get_2 + c.api('GET', '/apps/'+c.params['app']+'?raw', {}, function(app_data) { + c.api('GET', '/domains', {}, function(domain_data) { // Display a list of available domains var domains = []; @@ -695,14 +674,9 @@ y18n.t('confirm_app_change_url', [c.params['app']]), function() { params = {'domain': c.params['domain'], 'path': c.params['path']}; - c.api('/apps/' + c.params['app'] + '/changeurl', function(data) { // Call changeurl API - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app']); - }, 'PUT', params); - }, - function() { - store.clear('slide'); - c.redirect('#/apps/'+ c.params['app'] + '/changeurl'); + c.api('PUT', '/apps/' + c.params['app'] + '/changeurl', params, function(data) { + c.redirect_to('#/apps/'+ c.params['app']); + }); } ); }); diff --git a/src/js/yunohost/controllers/backup.js b/src/js/yunohost/controllers/backup.js index a4f77b27..42a5d455 100644 --- a/src/js/yunohost/controllers/backup.js +++ b/src/js/yunohost/controllers/backup.js @@ -32,118 +32,9 @@ c.view('backup/backup', {'storages':storages}); }); - // Storage list - app.get('#/storages/create', function (c) { - c.view('backup/storage_create', {}); - }); - - // Create a storage - app.post('#/storages', function (c) { - store.clear('slide'); - c.redirect('#/storages'); - }); - - // Create a backup - app.get('#/backup/:storage/create', function (c) { - var data = []; - data['storage'] = { - id:c.params['storage'], - name:y18n.t('local_archives') - }; - c.api('/hooks/backup', function(hooks) { - data['hooks'] = c.groupHooks(hooks['hooks']); - data['apps'] = {}; - c.api('/apps?with_backup', function(apps_list) { - data['apps'] = apps_list.apps; - c.view('backup/backup_create', data, c.selectAllOrNone); - }); - }); - }); - - - app.post('#/backup/:storage', function (c) { - var params = c.ungroupHooks(c.params['system_parts'],c.params['apps']); - c.api('/backup', function() { - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']); - }, 'POST', params); - }); - - // Restore a backup - app.post('#/backup/:storage/:archive/restore', function (c) { - c.confirm( - y18n.t('backup'), - y18n.t('confirm_restore', [c.params['archive']]), - $.proxy(function(c){ - var params = c.ungroupHooks(c.params['system_parts'],c.params['apps']); - params['force'] = ''; - c.api('/backup/restore/'+c.params['archive'], function(data) { - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - }, 'POST', params); - }, this, c), - function(){ - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - } - ); - }); - - // Delete a backup - app.get('#/backup/:storage/:archive/delete', function (c) { - c.confirm( - y18n.t('backup'), - y18n.t('confirm_delete', [c.params['archive']]), - function(){ - c.api('/backup/archives/'+c.params['archive'], function(data) { - c.redirect('#/backup/'+ c.params['storage']); - }, 'DELETE'); - }, - function(){ - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - } - ); - }); - - // Download a backup - app.get('#/backup/:storage/:archive/download', function (c) { - c.api('/backup/'+c.params['archive']+'/download', function(data) { - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - }, 'GET'); - }); - - // Copy a backup - app.get('#/backup/:storage/:archive/copy', function (c) { - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - }); - - // Upload a backup - app.get('#/backup/:storage/:archive/upload', function (c) { - store.clear('slide'); - c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); - }); - - // Get archive info - app.get('#/backup/:storage/:archive', function (c) { - c.api('/backup/archives/'+c.params['archive']+'?with_details', function(data) { - data.storage = { - id: c.params['storage'], - name: y18n.t('local_archives') - }; - data.other_storages = []; - data.name = c.params['archive']; - data.system_parts = c.groupHooks(Object.keys(data['system']),data['system']); - data.items = (data['system']!={} || data['apps']!=[]); - data.locale = y18n.locale - c.view('backup/backup_info', data, c.selectAllOrNone); - }); - }); - // Archive list app.get('#/backup/:storage', function (c) { - c.api('/backup/archives?with_info', function(data) { + c.api('GET', '/backup/archives?with_info', {}, function(data) { data.storage = { id: 'local', name: y18n.t('local_archives') @@ -159,4 +50,173 @@ }); }); + // View to create a backup + app.get('#/backup/:storage/create', function (c) { + var data = []; + data['storage'] = { + id:c.params['storage'], + name:y18n.t('local_archives') + }; + c.api('GET', '/hooks/backup', {}, function(hooks) { + data['hooks'] = groupHooks(hooks['hooks']); + data['apps'] = {}; + c.api('GET', '/apps?with_backup', {}, function(apps_list) { + data['apps'] = apps_list.apps; + c.view('backup/backup_create', data, function() { + + // Configure buttons "select all" and "select none" + + // Remove active style from buttons + $(".select_all-none input").click(function(){ $(this).toggleClass("active"); }); + // Select all checkbox in this panel + $(".select_all").click(function(){ + $(this).parents(".panel").children(".list-group").find("input").prop("checked", true); + }); + // Deselect all checkbox in this panel + $(".select_none").click(function(){ + $(this).parents(".panel").children(".list-group").find("input").prop("checked", false); + }); + }); + }); + }); + }); + + // Actually creating the backup + app.post('#/backup/:storage', function (c) { + var params = ungroupHooks(c.params['system_parts'],c.params['apps']); + c.api('POST', '/backup', params, function() { + c.redirect_to('#/backup/'+ c.params['storage']); + }); + }); + + // Get archive info + app.get('#/backup/:storage/:archive', function (c) { + c.api('GET', '/backup/archives/'+c.params['archive']+'?with_details', {}, function(data) { + data.storage = { + id: c.params['storage'], + name: y18n.t('local_archives') + }; + data.name = c.params['archive']; + data.system_parts = groupHooks(Object.keys(data['system']),data['system']); + data.items = (data['system']!={} || data['apps']!=[]); + data.locale = y18n.locale; + c.view('backup/backup_info', data, function() { + + // Configure buttons "select all" and "select none" + + // Remove active style from buttons + $(".select_all-none input").click(function(){ $(this).toggleClass("active"); }); + // Select all checkbox in this panel + $(".select_all").click(function(){ + $(this).parents(".panel").children(".list-group").find("input").prop("checked", true); + }); + // Deselect all checkbox in this panel + $(".select_none").click(function(){ + $(this).parents(".panel").children(".list-group").find("input").prop("checked", false); + }); + + // Delete button + $('button[data-action="delete"]').on('click', function() { + var storage = $(this).data('storage'); + var archive = $(this).data('archive'); + c.confirm( + y18n.t('backup'), + y18n.t('confirm_delete', [archive]), + function(){ + c.api('DELETE', '/backup/archives/'+archive, {}, function(data) { + c.redirect_to('#/backup/'+ storage); + }); + } + ); + }); + }); + }); + }); + + // Restore a backup + app.post('#/backup/:storage/:archive/restore', function (c) { + c.confirm( + y18n.t('backup'), + y18n.t('confirm_restore', [c.params['archive']]), + $.proxy(function(c){ + var params = ungroupHooks(c.params['system_parts'],c.params['apps']); + params['force'] = ''; + c.api('POST', '/backup/restore/'+c.params['archive'], params, function(data) { + c.redirect_to('#/backup/'+ c.params['storage']+'/'+c.params['archive']); + }); + }, this, c) + ); + }); + + function groupHooks(hooks, raw_infos) { + var data = {}; + var rules = [ + { + id:'configuration', + isIn:function (hook) { + return hook.indexOf('conf_')==0 + } + } + ]; + + $.each(hooks, function(i, hook) { + var group_id=hook; + var hook_size=(raw_infos && raw_infos[hook] && raw_infos[hook].size)?raw_infos[hook].size:0; + $.each(rules, function(i, rule) { + if (rule.isIn(hook)) { + group_id = 'adminjs_group_'+rule.id; + return false; + } + }); + + if(group_id in data) { + data[group_id] = { + name:y18n.t('hook_'+group_id), + value:data[group_id].value+','+hook, + description:data[group_id].description+', '+y18n.t('hook_'+hook), + size:data[group_id].size + hook_size + }; + } + else { + data[group_id] = { + name:y18n.t('hook_'+group_id), + value:hook, + description:(group_id==hook)?y18n.t('hook_'+hook+'_desc'):y18n.t('hook_'+hook), + size:hook_size + }; + } + }); + return data; + }; + + function ungroupHooks(system_parts, apps) { + + var data = {}; + data['apps'] = apps || []; + data['system'] = system_parts || []; + + if (data['system'].constructor !== Array) { + data['system'] = [data['system']]; + } + if (data['apps'].constructor !== Array) { + data['apps'] = [data['apps']]; + } + + // Some hook value contains multiple hooks separated by commas + var split_hooks = []; + $.each(data['system'], function(i, hook) { + split_hooks = split_hooks.concat(hook.split(',')); + }); + data['system'] = split_hooks; + + if (data['system'].length == 0) { + delete data['system']; + } + if (data['apps'].length == 0) { + delete data['apps']; + } + return data; + }; + + })(); diff --git a/src/js/yunohost/controllers/diagnosis.js b/src/js/yunohost/controllers/diagnosis.js new file mode 100644 index 00000000..6fb48f7b --- /dev/null +++ b/src/js/yunohost/controllers/diagnosis.js @@ -0,0 +1,100 @@ +(function() { + // Get application context + var app = Sammy.apps['#main']; + var store = app.store; + + // ********* + // Diagnosis + // ********* + + app.get('#/diagnosis', function (c) { + c.api('GET', '/diagnosis/show?full', {}, function(data) { + + // Prepare data to be displayed ... + for (var i = 0 ; i < data.reports.length ; i++) + { + // Convert timestamp to datetime + data.reports[i].time = new Date(data.reports[i].timestamp*1000); + data.reports[i].warnings = 0; + data.reports[i].errors = 0; + data.reports[i].ignored = 0; + for (var j = 0 ; j < data.reports[i].items.length ; j++) + { + var type_ = data.reports[i].items[j].status; + type_ = type_.toLowerCase(); + var ignored = data.reports[i].items[j].ignored; + var icon = ""; + var issue = false; + + if (type_ == "success") { + icon = "check-circle"; + } + else if (ignored == true) { + icon = type_; + if (type_ == "error") { + icon = "times" + } + type_ = "ignored"; + data.reports[i].ignored++; + } + else if (type_ == "warning") { + icon = "warning"; + issue = true; + data.reports[i].warnings++; + } + else if (type_ == "error") { + type_ = "danger"; + icon = "times"; + issue = true; + data.reports[i].errors++; + } + data.reports[i].items[j].status = type_; + data.reports[i].items[j].icon = icon; + data.reports[i].items[j].issue = issue; + // We want filter_args to be something like "dnsrecords,domain=yolo.test,category=xmpp" + data.reports[i].items[j].filter_args = data.reports[i].id; + for (prop in data.reports[i].items[j].meta) { + data.reports[i].items[j].filter_args = data.reports[i].items[j].filter_args + ","+prop+"="+data.reports[i].items[j].meta[prop]; + } + }; + data.reports[i].noIssues = data.reports[i].warnings + data.reports[i].errors ? false : true; + }; + + // Render and display the view + c.view('diagnosis/diagnosis_show', data, function() { + + // Configure share with yunopaste button + $("button[data-action='share']").click(function() { + c.api('GET', '/diagnosis/show?share', {}, function(data) { + c.hideLoader(); + window.open(data.url, '_blank'); + }); + }); + + // Configure 'rerun diagnosis' button behavior + $("button[data-action='rerun-diagnosis']").click(function() { + var category = $(this).data("category"); + c.api('POST', '/diagnosis/run?force', {"categories": [category]}, function(data) { + c.refresh(); + }); + }); + + // Configure 'ignore' / 'unignore' buttons behavior + $("button[data-action='ignore']").click(function() { + var filter_args = $(this).data("filter-args"); + c.api('POST', '/diagnosis/ignore', {'add_filter': filter_args.split(',') }, function(data) { + c.refresh(); + }) + }); + + $("button[data-action='unignore']").click(function() { + var filter_args = $(this).data("filter-args"); + c.api('POST', '/diagnosis/ignore', {'remove_filter': filter_args.split(',') }, function(data) { + c.refresh(); + }) + }); + }); + }); + }); + +})(); diff --git a/src/js/yunohost/controllers/domains.js b/src/js/yunohost/controllers/domains.js index 259b8b08..51850f90 100644 --- a/src/js/yunohost/controllers/domains.js +++ b/src/js/yunohost/controllers/domains.js @@ -10,8 +10,8 @@ // List existing domains app.get('#/domains', function (c) { - c.api('/domains', function(data) { // http://api.yunohost.org/#!/domain/domain_list_get_2 - c.api('/domains/main', function(data2) { + c.api('GET', '/domains', {}, function(data) { + c.api('PUT', '/domains/main', {}, function(data2) { var domains = []; $.each(data.domains, function(k, domain) { domains.push({ @@ -29,7 +29,7 @@ domains: domains, main_domain_form: main_domain_form }); - }, 'PUT'); + }); }); }); @@ -68,8 +68,7 @@ if (c.params['domain'] === '') { if (c.params['ddomain'] === '') { c.flash('fail', y18n.t('error_select_domain')); - store.clear('slide'); - c.redirect('#/domains/add'); + c.redirect_to('#/domains/add'); } params.domain = c.params['ddomain'] + c.params['ddomain-ext']; endurl = 'dyndns'; @@ -77,42 +76,56 @@ params.domain = c.params['domain']; } - c.api('/domains?'+endurl, function(data) { // http://api.yunohost.org/#!/domain/domain_add_post_1 - c.redirect('#/domains'); - }, 'POST', params); + c.api('POST', '/domains?'+endurl, params, function(data) { + c.redirect_to('#/domains'); + }); }); // Get existing domain info app.get('#/domains/:domain', function (c) { - c.api('/domains/main', function(dataMain) { - c.api('/apps?installed', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 - - // FIXME - This dirty trick (along with the previous API call - // for apps installed) should be removed once letsencrypt_ynh - // is not used by many people anymore. Probably around 07/2017 - // or end of 2017... - var enable_cert_management_ = true; - $.each(data['apps'], function(k, v) { - if (v.id == "letsencrypt") { - enable_cert_management_ = false; - } - }); - + c.api('PUT', '/domains/main', {}, function(dataMain) { + c.api('GET', '/apps?installed', {}, function(data) { var domain = { name: c.params['domain'], main: (c.params['domain'] == dataMain.current_main_domain) ? true : false, - url: "https://"+c.params['domain'], - enable_cert_management: enable_cert_management_ + url: "https://"+c.params['domain'] }; - c.view('domain/domain_info', domain); + c.view('domain/domain_info', domain, function() { + + // Configure "set default" button + $('button[data-action="set_default"]').on("click", function() { + var domain = $(this).data("domain"); + c.confirm( + y18n.t('domains'), + y18n.t('confirm_change_maindomain'), + function() { + c.api('PUT', '/domains/main', {new_main_domain: domain}, function() { c.refresh() }); + } + ) + }); + + // Configure delete button + $('button[data-action="delete"]').on("click", function() { + var domain = $(this).data("domain"); + c.confirm( + y18n.t('domains'), + y18n.t('confirm_delete', [domain]), + function(){ + c.api('DELETE', '/domains/'+ domain, {}, function(data) { + c.redirect_to('#/domains'); + }); + } + ); + }); + }); }); - }, 'PUT'); + }); }); // Domain DNS app.get('#/domains/:domain/dns', function (c) { - c.api('/domains/' + c.params['domain'] + '/dns', function(data) { + c.api('GET', '/domains/' + c.params['domain'] + '/dns', {}, function(data) { var domain = { name: c.params['domain'], dns: data @@ -123,7 +136,7 @@ // Domain certificate app.get('#/domains/:domain/cert-management', function (c) { - c.api('/domains/cert-status/' + c.params['domain'] + '?full', function(data) { + c.api('GET', '/domains/cert-status/' + c.params['domain'] + '?full', {}, function(data) { var s = data["certificates"][c.params['domain']]; var status_ = { @@ -199,132 +212,45 @@ status: status_, actions_enabled : actions_enabled }; - c.view('domain/domain_cert', data_); + + c.view('domain/domain_cert', data_, function() { + // Configure install / renew buttons behavior + $("button[data-action]").on("click", function () { + var action = $(this).data("action"), + domain = $(this).data("domain"), + confirm_key = "", + api_url = ""; + + switch (action) { + case 'install-LE': + confirm_key = 'confirm_cert_install_LE'; + api_url = '/domains/cert-install/' + domain; + break; + case 'regen-selfsigned': + confirm_key = 'confirm_cert_regen_selfsigned'; + api_url = '/domains/cert-install/' + domain + "?self_signed"; + break; + case 'renew-letsencrypt': + confirm_key = 'confirm_cert_manual_renew_LE'; + api_url = '/domains/cert-renew/' + domain + "?force"; + break; + case 'replace-with-selfsigned': + confirm_key = 'confirm_cert_revert_to_selfsigned'; + api_url = '/domains/cert-install/' + domain + "?self_signed&force"; + break; + default: + c.flash('fail', y18n.t('unknown_action', [action])); + return + } + + c.confirm( + y18n.t('certificate'), + y18n.t(confirm_key, [domain]), + function(){ c.api('POST', api_url, {}, function() { c.refresh() }); } + ); + }); + }); }); }); - // Install let's encrypt certificate on domain - app.get('#/domains/:domain/cert-install-LE', function (c) { - c.confirm( - y18n.t('certificate'), - y18n.t('confirm_cert_install_LE', [c.params['domain']]), - function(){ - c.api('/domains/cert-install/' + c.params['domain'], function(data) { - store.clear('slide'); - c.redirect('#/domains/'+c.params['domain']+'/cert-management'); - }, 'POST'); - }, - function(){ - store.clear('slide'); - c.redirect('#/domains/'+c.params['domain']+'/cert-management'); - } - ); - }); - - // Regenerate a self-signed certificate - app.get('#/domains/:domain/cert-regen-selfsigned', function (c) { - c.confirm( - y18n.t('certificate'), - y18n.t('confirm_cert_regen_selfsigned', [c.params['domain']]), - function(){ - c.api('/domains/cert-install/' + c.params['domain'] + "?self_signed", function(data) { - store.clear('slide'); - c.redirect('#/domains/'+c.params['domain']+'/cert-management'); - }, 'POST'); - }, - function(){ - store.clear('slide'); - c.redirect('#/domains/'+c.params['domain']+'/cert-management'); - } - ); - }); - - // Manually renew a Let's Encrypt certificate - app.get('#/domains/:domain/cert-renew-letsencrypt', function (c) { - c.confirm( - y18n.t('certificate'), - y18n.t('confirm_cert_manual_renew_LE', [c.params['domain']]), - function(){ - c.api('/domains/cert-renew/' + c.params['domain'] + "?force", function(data) { - store.clear('slide'); - c.redirect('#/domains/'+c.params['domain']+'/cert-management'); - }, 'POST'); - }, - function(){ - store.clear('slide'); - c.redirect('#/domains/'+c.params['domain']+'/cert-management'); - } - ); - }); - - // Replace valid cert with self-signed - app.get('#/domains/:domain/cert-replace-with-selfsigned', function (c) { - c.confirm( - y18n.t('certificate'), - y18n.t('confirm_cert_revert_to_selfsigned', [c.params['domain']]), - function(){ - c.api('/domains/cert-install/' + c.params['domain'] + "?self_signed&force", function(data) { - store.clear('slide'); - c.redirect('#/domains/'+c.params['domain']+'/cert-management'); - }, 'POST'); - }, - function(){ - store.clear('slide'); - c.redirect('#/domains/'+c.params['domain']+'/cert-management'); - } - ); - }); - - - // Remove existing domain - app.get('#/domains/:domain/delete', function (c) { - c.confirm( - y18n.t('domains'), - y18n.t('confirm_delete', [c.params['domain']]), - function(){ - c.api('/domains/'+ c.params['domain'], function(data) { // http://api.yunohost.org/#!/domain/domain_remove_delete_3 - store.clear('slide'); - c.redirect('#/domains'); - }, 'DELETE'); - }, - function(){ - store.clear('slide'); - c.redirect('#/domains'); - } - ); - }); - - // Set default domain - app.post('#/domains', function (c) { - if (c.params['domain'] === '') { - c.flash('fail', y18n.t('error_select_domain')); - store.clear('slide'); - c.redirect('#/domains'); - } else { - c.confirm( - y18n.t('domains'), - y18n.t('confirm_change_maindomain'), - function(){ - var params = { - new_domain: c.params['domain'] - }; - c.api('/domains/main', function(data) { // http://api.yunohost.org/#!/tools/tools_maindomain_put_1 - store.clear('slide'); - c.redirect('#/domains'); - }, 'PUT', params); - - // Wait 15s and refresh the page - var refreshDomain = window.setTimeout(function(){ - store.clear('slide'); - c.redirect('#/domains'); - }, 15000); - }, - function(){ - store.clear('slide'); - c.redirect('#/domains'); - } - ); - } - }); - })(); diff --git a/src/js/yunohost/controllers/firewall.js b/src/js/yunohost/controllers/firewall.js index beeff865..013d2511 100644 --- a/src/js/yunohost/controllers/firewall.js +++ b/src/js/yunohost/controllers/firewall.js @@ -10,7 +10,7 @@ // Firewall status app.get('#/tools/firewall', function (c) { - c.api('/firewall?raw', function(data) { + c.api('GET', '/firewall?raw', {}, function(data) { var firewall = { ports: {}, upnp: false @@ -30,28 +30,49 @@ // Get UPnP status firewall.upnp = data.uPnP.enabled; - c.view('tools/tools_firewall', firewall); + c.view('tools/tools_firewall', firewall, function() { + + // Buttons in the 'ports' panel to open/close specific ports + $("button[data-port]").on("click", function() { + + var port = $(this).data("port"); + var action = $(this).data("action"); + var protocol = $(this).data("protocol"); + var connection = $(this).data("connection"); + c.confirm( + y18n.t('firewall'), + // confirm_firewall_open and confirm_firewall_close + y18n.t('confirm_firewall_' + action, [ port, y18n.t(protocol), y18n.t(connection)]), + function(){ c.togglePort(port, protocol, connection, action); } + ); + }); + + // Buttons to enable / disable UPnP + $("button[data-upnp]").on("click", function() { + var action = $(this).data("upnp"); + c.confirm( + y18n.t('firewall'), + // confirm_upnp_enable and confirm_upnp_disable + y18n.t('confirm_upnp_' + action), + function(){ c.api('GET', '/firewall/upnp', {action: action}, function() { c.refresh() }); } + ); + }); + }); }); }); - // Enable/Disable UPnP - app.get('#/tools/firewall/upnp/:action', function (c) { + // Update port status from form + app.post('#/tools/firewall/port', function (c) { c.confirm( y18n.t('firewall'), - // confirm_upnp_enable and confirm_upnp_disable - y18n.t('confirm_upnp_' + c.params['action'].toLowerCase()), + y18n.t('confirm_firewall_' + c.params['action'].toLowerCase(), [ c.params['port'], y18n.t(c.params['protocol']), y18n.t(c.params['connection']) ]), function(){ - var params = { - action : c.params['action'] - }; - c.api('/firewall/upnp', function(data) { - store.clear('slide'); - c.redirect('#/tools/firewall'); - }, 'GET', params); - }, - function(){ - store.clear('slide'); - c.redirect('#/tools/firewall'); + c.togglePort( + c.params['port'], + c.params['protocol'], + c.params['connection'], + c.params['action'] + ); } ); }); @@ -65,8 +86,7 @@ if (port != parseInt(port) || port < 0 || port > 65535) { c.flash('fail', y18n.t('unknown_argument', [port])); - store.clear('slide'); - c.redirect('#/tools/firewall'); + c.refresh(); } switch (connection) { @@ -98,75 +118,27 @@ break; default: c.flash('fail', y18n.t('unknown_action', [action])); - store.clear('slide'); - c.redirect('#/tools/firewall'); + c.refresh(); } - if (method !== null && protocol !== null && port !== null) { - // port: - // protocol: - // - UDP - // - TCP - // - Both - // --ipv4-only: - // --ipv6-only: - // --no-upnp: - var params = { - port : port, - protocol : protocol - }; - c.api('/firewall/port?'+endurl, function(data) { - store.clear('slide'); - c.redirect('#/tools/firewall'); - }, method, params); - } - else { - store.clear('slide'); - c.redirect('#/tools/firewall'); - } + // port: + // protocol: + // - UDP + // - TCP + // - Both + // --ipv4-only: + // --ipv6-only: + // --no-upnp: + var params = { + port : port, + protocol : protocol + }; + + c.api(method, '/firewall/port?'+endurl, params, function() { c.refresh() }); + + return; }); - // Update port status from direct link - // #/firewall/port/{{@key}}/tcp/ipv4/close - app.get('#/tools/firewall/port/:port/:protocol/:connection/:action', function (c) { - c.confirm( - y18n.t('firewall'), - // confirm_firewall_open and confirm_firewall_close - y18n.t( 'confirm_firewall_' + c.params['action'].toLowerCase(), [ c.params['port'], y18n.t(c.params['protocol']), y18n.t(c.params['connection'])]), - function(){ - c.togglePort( - c.params['port'], - c.params['protocol'], - c.params['connection'], - c.params['action'] - ); - }, - function(){ - store.clear('slide'); - c.redirect('#/tools/firewall'); - } - ); - }); - // Update port status from form - app.post('#/tools/firewall/port', function (c) { - c.confirm( - y18n.t('firewall'), - y18n.t('confirm_firewall_' + c.params['action'].toLowerCase(), [ c.params['port'], y18n.t(c.params['protocol']), y18n.t(c.params['connection']) ]), - function(){ - c.togglePort( - c.params['port'], - c.params['protocol'], - c.params['connection'], - c.params['action'] - ); - }, - function(){ - store.clear('slide'); - c.redirect('#/tools/firewall'); - } - ); - }); - -})(); \ No newline at end of file +})(); diff --git a/src/js/yunohost/controllers/home.js b/src/js/yunohost/controllers/home.js index ac315c21..9038ed76 100644 --- a/src/js/yunohost/controllers/home.js +++ b/src/js/yunohost/controllers/home.js @@ -24,45 +24,36 @@ $('#masthead').show() .find('.logout-btn').hide(); store.set('path-1', '#/login'); - if ($('div.loader').length === 0) { - $('#main').append('
'); + + c.showLoader(); + + // We gonna retry 3 times to check if yunohost is installed + if (app.isInstalledTry === undefined) { + app.isInstalledTry = 3; } c.checkInstall(function(isInstalled) { + if (isInstalled) { - // Remove loader - $('div.loader').remove(); - // Pass domain to hide form field c.view('login', { 'domain': window.location.hostname }); - } else if (typeof isInstalled === 'undefined') { - if (app.isInstalledTry > 0) { - app.isInstalledTry--; - app.loaded = false; // Show pacman - setTimeout(function() { - c.redirect('#/'); - }, 5000); - } - else { - // Reset count - app.isInstalledTry = 3; + return; + } - // API is not responding after 3 try - $( document ).ajaxError(function( event, request, settings ) { - // Display error if status != 200. - // .ajaxError fire even with status code 200 because json is sometimes not valid. - if (request.status !== 200) c.flash('fail', y18n.t('api_not_responding', [request.status+' '+request.statusText] )); - - // Unbind directly - $(document).off('ajaxError'); - }); - - // Remove pacman - app.loaded = true; - $('div.loader').remove(); - } - } else { - $('div.loader').remove(); + if (typeof isInstalled !== 'undefined') { c.redirect('#/postinstall'); + return; + } + + // If the retry counter is still up, retry this function 5 sec + // later + if (app.isInstalledTry > 0) { + app.isInstalledTry--; + setTimeout(function() { + c.redirect('#/'); + }, 5000); + } + else { + c.flash('fail', y18n.t('api_not_responding')); } }); }); @@ -80,7 +71,7 @@ var params = { password: c.params['password'] }; - c.api('/login', function(data) { + c.api('POST', '/login', params, function(data) { store.set('connected', true); c.trigger('login'); $('#masthead .logout-btn').fadeIn(); @@ -90,19 +81,19 @@ } else { c.redirect('#/'); } - }, 'POST', params, false); + }, undefined, false); }); app.get('#/logout', function (c) { - c.api('/logout', function (data) { + c.api('GET', '/logout', {}, function (data) { store.clear('url'); store.clear('connected'); store.set('path', '#/'); c.trigger('logout'); c.flash('success', y18n.t('logged_out')); c.redirect('#/login'); - }, 'GET', {}, false); + }, undefined, false); }); })(); diff --git a/src/js/yunohost/controllers/monitor.js b/src/js/yunohost/controllers/monitor.js deleted file mode 100644 index 527e466c..00000000 --- a/src/js/yunohost/controllers/monitor.js +++ /dev/null @@ -1,50 +0,0 @@ -(function() { - // Get application context - var app = Sammy.apps['#main']; - var store = app.store; - - /** - * Monitor - * - */ - - // Server monitoring - app.get('#/tools/monitor', function (c) { - var monitorData = {}; - - // Why this method ? - c.api('/services/glances', function(data) { // ? - monitorData.status = true; - - if (data.status == 'running') { - c.api('/monitor/system', function(data) { - monitorData.system = data; - - c.api('/monitor/disk', function(data) { - monitorData.disk = data; - - c.api('/monitor/network', function(data) { - monitorData.network = data; - - // Remove useless interface - delete monitorData.network.usage.lo; - - // Get YunoHost versions too - c.api('/diagnosis', function(diagnosis) { - monitorData.versions = diagnosis.packages; - c.view('tools/tools_monitoring', monitorData); - }); - }); - - }); - }); - } - else { - monitorData.status = false; - c.view('tools/tools_monitoring', monitorData); - } - - }, 'GET'); - }); - -})(); diff --git a/src/js/yunohost/controllers/postinstall.js b/src/js/yunohost/controllers/postinstall.js index 04f7d7e9..d8fc96a7 100644 --- a/src/js/yunohost/controllers/postinstall.js +++ b/src/js/yunohost/controllers/postinstall.js @@ -13,7 +13,7 @@ $('#masthead').hide(); c.checkInstall(function(isInstalled) { if (isInstalled || typeof isInstalled === 'undefined') { - c.redirect('#/login'); + c.redirect_to('#/login'); } else { c.view('postinstall/postinstall_1'); } @@ -41,7 +41,6 @@ if ($('#domain').val() === '') { if ($('#ddomain').val() === '') { e.preventDefault(); - store.clear('slide'); c.flash('fail', y18n.t('error_select_domain')); } else { domain = $('#ddomain').val() + $('select[name="ddomain-ext"]').val(); @@ -51,7 +50,7 @@ } store.set('maindomain', domain); }); - }, false); // We disable enableSlide because that causes some issues with accordion when using the 'previous' button + }); }); }); @@ -59,49 +58,52 @@ app.get('#/postinstall/password', function(c) { $('#masthead').hide(); if (!store.get('maindomain')) { - store.clear('slide'); - c.redirect('#/postinstall/domain'); + c.redirect_to('#/postinstall/domain'); } else { - c.view('postinstall/postinstall_3', { 'domain': store.get('maindomain').toLowerCase() }, - function() { }, - false); // We disable enableSlide because that causes some issues with accordion when using the 'previous' button + c.view('postinstall/postinstall_3', { 'domain': store.get('maindomain').toLowerCase() }); } }); // Execute post-installation app.post('#/postinstall', function (c) { - if (c.params['password'] === '' || c.params['confirmation'] === '') { + + var password = c.params['password']; + var confirmation = c.params['confirmation']; + var domain = c.params['domain'].toLowerCase(); + + // Check password ain't empty + if (password === '' || confirmation === '') { c.flash('fail', y18n.t('password_empty')); + return; } - else if (c.params['password'] == c.params['confirmation']) { - if (c.params['domain'] === '') { - c.flash('fail', y18n.t('error_select_domain')); - store.clear('slide'); - c.redirect('#/postinstall/domain'); - } else { - var params = { - domain: c.params['domain'].toLowerCase() - }; - } - c.confirm( - y18n.t('postinstall'), - y18n.t('confirm_postinstall', [c.params['domain']]), - function(){ - params.password = c.params['password']; - - store.set('url', window.location.hostname +'/yunohost/api'); - store.set('user', 'admin'); - c.api('/postinstall', function(data) { // http://api.yunohost.org/#!/tools/tools_postinstall_post_0 - c.redirect('#/login'); - }, 'POST', params); - }, - function(){ - } - ); - } else { + // Check password matches confirmation + if (password !== confirmation) { c.flash('fail', y18n.t('passwords_dont_match')); + return; } + + // Check domain ain't empty... + if (domain === '') { + c.flash('fail', y18n.t('error_select_domain')); + c.redirect_to('#/postinstall/domain', {slide: false}); + return; + } + + // Ask confirmation to the user + c.confirm( + y18n.t('postinstall'), + y18n.t('confirm_postinstall', [c.params['domain']]), + // Start the actual postinstall process + function(){ + store.set('url', window.location.hostname +'/yunohost/api'); + store.set('user', 'admin'); + c.api('POST', '/postinstall', {domain: domain, password: password}, function() { + c.flash('success', y18n.t('installation_complete')); + c.redirect_to('#/login'); + }); + } + ); }); })(); diff --git a/src/js/yunohost/controllers/services.js b/src/js/yunohost/controllers/services.js index e3de3554..113c34fc 100644 --- a/src/js/yunohost/controllers/services.js +++ b/src/js/yunohost/controllers/services.js @@ -10,7 +10,7 @@ // All services status app.get('#/services', function (c) { - c.api('/services', function(data) { // ? + c.api('GET', '/services', {}, function(data) { var data2 = { services: [] }; @@ -45,7 +45,7 @@ // Status & actions for a service app.get('#/services/:service', function (c) { - c.api('/services/'+ c.params['service'], function(data) { // ? + c.api('GET', '/services/'+ c.params['service'], {}, function(data) { var data2 = { service: data }; @@ -64,9 +64,45 @@ { data2.service.active_at = 0; } - store.clear('slide'); - c.view('service/service_info', data2); - }, 'GET'); + c.view('service/service_info', data2, function() { + + // Configure behavior for enable/disable and start/stop buttons + $('button[data-action]').on('click', function() { + + var service = $(this).data('service'); + var action = $(this).data('action'); + + c.confirm("Service", y18n.t('confirm_service_' + action, [service]), function(){ + + var method = null, + endurl = service; + + switch (action) { + case 'start': + method = 'PUT'; + break; + case 'stop': + method = 'DELETE'; + break; + case 'enable': + method = 'PUT'; + endurl += '/enable'; + break; + case 'disable': + method = 'DELETE'; + endurl += '/enable'; + break; + default: + c.flash('fail', y18n.t('unknown_action', [action])); + c.refresh(); + return; + } + + c.api(method, '/services/'+ endurl, {}, function() { c.refresh(); }); + }); + }); + }); + }); }); // Service log @@ -74,63 +110,14 @@ var params = { number: 50 }; - c.api('/services/'+ c.params['service'] +'/log', function(data) { // ? + c.api('GET', '/services/'+ c.params['service'] +'/log', params, function(data) { // ? data2 = { 'logs': [], 'name': c.params['service'] }; $.each(data, function(k, v) { data2.logs.push({filename: k, filecontent: v.join('\n')}); }); c.view('service/service_log', data2); - }, 'GET', params); - }); - - // Enable/Disable & Start/Stop service - app.get('#/services/:service/:action', function (c) { - c.confirm( - "Service", - // confirm_service_start, confirm_service_stop, confirm_service_enable and confirm_service_disable - y18n.t('confirm_service_' + c.params['action'].toLowerCase(), [c.params['service']]), - function(){ - var method = null, - endurl = c.params['service']; - - switch (c.params['action']) { - case 'start': - method = 'PUT'; - break; - case 'stop': - method = 'DELETE'; - break; - case 'enable': - method = 'PUT'; - endurl += '/enable'; - break; - case 'disable': - method = 'DELETE'; - endurl += '/enable'; - break; - default: - c.flash('fail', y18n.t('unknown_action', [c.params['action']])); - store.clear('slide'); - c.redirect('#/services/'+ c.params['service']); - } - - if (method && endurl) { - c.api('/services/'+ endurl, function(data) { - store.clear('slide'); - c.redirect('#/services/'+ c.params['service']); - }, method); - } - else { - store.clear('slide'); - c.redirect('#/services/'+ c.params['service']); - } - }, - function(){ - store.clear('slide'); - c.redirect('#/services/'+ c.params['service']); - } - ); + }); }); })(); diff --git a/src/js/yunohost/controllers/tools.js b/src/js/yunohost/controllers/tools.js index cde3a447..92a9d8d8 100644 --- a/src/js/yunohost/controllers/tools.js +++ b/src/js/yunohost/controllers/tools.js @@ -26,77 +26,69 @@ }); if ($.isEmptyObject(params)) { c.flash('fail', y18n.t('error_modify_something')); - store.clear('slide'); - c.redirect('#/tools/adminpw'); - } else if (params['new_password'] !== params['confirm_new_password']) { - c.flash('fail', y18n.t('passwords_dont_match')); - store.clear('slide'); - c.redirect('#/tools/adminpw'); - } else { - c.api('/login', function(data) { - // Remove useless variable - delete params['old_password']; - delete params['confirm_new_password']; - - // Update password and redirect to the home - c.api('/adminpw', function(data) { // http://api.yunohost.org/#!/tools/tools_adminpw_put_3 - c.redirect('#/logout'); - }, 'PUT', params); - }, 'POST', { 'password': params['old_password'] }, false); + c.refresh(); + return; } + if (params['new_password'] !== params['confirm_new_password']) { + c.flash('fail', y18n.t('passwords_dont_match')); + c.refresh(); + return; + } + + c.api('POST', '/login', { 'password': params['old_password'] }, function(data) { + // Remove useless variable + delete params['old_password']; + delete params['confirm_new_password']; + + // Update password and redirect to the home + c.api('PUT', '/adminpw', params, function(data) { + c.redirect_to('#/logout'); + }); + }, undefined, false); }); // System update & upgrade app.get('#/update', function (c) { - c.api('/update', function(data) { - c.view('update/update', data); - }, 'PUT'); - }); + c.api('PUT', '/update', {}, function(data) { + c.view('tools/tools_update', data, function() { + // Configure buttons behaviors + $("button[data-upgrade]").on("click", function() { - // Upgrade apps or packages - app.get('#/upgrade/:type', function (c) { - c.confirm( - y18n.t('tools'), - // confirm_update_apps and confirm_update_packages - y18n.t('confirm_update_' + c.params['type'].toLowerCase()), - function(){ - c.api('/upgrade?'+c.params["type"], - function(data) { - store.clear('slide'); - c.redirect('#/tools/logs'); - }, - 'PUT'); - }, - function(){ - store.clear('slide'); - c.redirect('#/update'); - } - ); - }); + var what = $(this).data("upgrade").toLowerCase(); - // Upgrade a specific apps - app.get('#/upgrade/apps/:app', function (c) { - c.confirm( - y18n.t('tools'), - y18n.t('confirm_update_specific_app', [c.params['app']]), - function(){ - c.api('/upgrade/apps?app='+c.params['app'].toLowerCase(), - function(data) { - store.clear('slide'); - c.redirect('#/tools/logs'); - }, - 'PUT'); - }, - function(){ - store.clear('slide'); - c.redirect('#/update'); - } - ); + // Upgrade all apps or the system + + if ((what == "system") || (what == "system")) + { + var confirm_message = y18n.t('confirm_update_' + what); + var api_url = '/upgrade?'+what; + } + + // Upgrade a specific apps + + else + { + var confirm_message = y18n.t('confirm_update_specific_app', [what]); + var api_url = '/upgrade/apps?app='+what; + } + + c.confirm( + y18n.t('tools'), + confirm_message, + function(){ + c.api('PUT', api_url, {}, function(data) { + c.redirect_to('#/tools/logs'); + }); + } + ); + }); + }); + }); }); // Display journals list app.get('#/tools/logs', function (c) { - c.api("/logs?limit=25&with_details", function(categories) { + c.api('GET', "/logs?limit=25&with_details", {}, function(categories) { data = []; category_icons = { 'operation': 'wrench', @@ -138,8 +130,8 @@ var params = "?path=" + c.params["splat"][0]; var number = (c.params["number"])?c.params["number"]:50; params += "&number=" + number; - - c.api("/logs/display" + params, function(log) { + + c.api('GET', "/logs/display" + params, {}, function(log) { if ('metadata' in log) { if (!'env' in log.metadata && 'args' in log.metadata) { log.metadata.env = log.metadata.args @@ -149,10 +141,19 @@ "log": log, "next_number": log.logs.length == number ? number * 10:false, "locale": y18n.locale + }, function() { + // Configure behavior for the button to share log on Yunohost (it calls display --share) + $('button[data-action="share"]').on("click", function() { + c.api('GET', '/logs/display?path='+$(this).data('log-id')+'&share', {}, + function(data) { + c.hideLoader(); + window.open(data.url, '_blank'); + }); + }); }); }); }); - + // Download SSL Certificate Authority app.get('#/tools/ca', function (c) { @@ -210,80 +211,50 @@ // Reboot or shutdown button app.get('#/tools/reboot', function (c) { - c.view('tools/tools_reboot'); - }); + c.view('tools/tools_reboot', {}, function() { + // Configure reboot/shutdown buttons behavior + $("button[data-action]").on("click", function() { + var action = $(this).data("action"); - // Reboot or shutdown actions - app.get('#/tools/reboot/:action', function (c) { - var action = c.params['action'].toLowerCase(); - if (action == 'reboot' || action == 'shutdown') { - c.confirm( - y18n.t('tools_' + action), - // confirm_reboot_action_reboot or confirm_reboot_action_shutdown - y18n.t('confirm_reboot_action_' + action), - function(){ - c.api('/'+action+'?force', function(data) { - // This code is not executed due to 502 response (reboot or shutdown) - c.redirect('#/logout'); - }, 'PUT', {}, false, function (xhr) { - c.flash('success', y18n.t('tools_' + action + '_done')) - // Disconnect from the webadmin - store.clear('url'); - store.clear('connected'); - store.set('path', '#/'); + c.confirm( + y18n.t('tools_' + action), + y18n.t('confirm_reboot_action_' + action), + function(){ + c.api('PUT', '/'+action+'?force', {}, function(data) { + // This code is not executed due to 502 response (reboot or shutdown) + c.redirect_to('#/logout'); + }, function (xhr) { + c.flash('success', y18n.t('tools_' + action + '_done')) + // Disconnect from the webadmin + store.clear('url'); + store.clear('connected'); + store.set('path', '#/'); - // Rename the page to allow refresh without ask for rebooting - window.location.href = window.location.href.split('#')[0] + '#/'; - // Display reboot or shutdown info - // We can't use template because now the webserver is off - if (action == 'reboot') { - $('#main').replaceWith('
' + y18n.t('tools_rebooting') + '
'); - } - else { - $('#main').replaceWith('
' + y18n.t('tools_shuttingdown') + '
'); - } + // Rename the page to allow refresh without ask for rebooting + window.location.href = window.location.href.split('#')[0] + '#/'; + // Display reboot or shutdown info + // We can't use template because now the webserver is off + if (action == 'reboot') { + $('#main').replaceWith('
' + y18n.t('tools_rebooting') + '
'); + } + else { + $('#main').replaceWith('
' + y18n.t('tools_shuttingdown') + '
'); + } - // Remove loader if any - $('div.loader').remove(); + c.hideLoader(); - // Force scrollTop on page load - $('html, body').scrollTop(0); - store.clear('slide'); - }); - - }, - function(){ - store.clear('slide'); - c.redirect('#/tools/reboot'); - } - ); - } - else { - c.flash('fail', y18n.t('unknown_action', [action])); - store.clear('slide'); - c.redirect('#/tools/reboot'); - } - }); - - // Diagnosis - app.get('#/tools/diagnosis(/:private)?', function (c) { - // See http://sammyjs.org/docs/routes for splat documentation - var private = (c.params.splat[0] == 'private'); - - var endurl = (private) ? '?private' : ''; - c.api('/diagnosis'+endurl, function(diagnosis) { - c.view('tools/tools_diagnosis', { - 'diagnosis' : JSON.stringify(diagnosis, undefined, 4), - 'raw' : diagnosis, - 'private' : private + // Force scrollTop on page load + $('html, body').scrollTop(0); + }, false); + }); }); }); }); - // Reboot or shutdown button + // Migrations app.get('#/tools/migrations', function (c) { - c.api('/migrations?pending', function(pending_migrations) { - c.api('/migrations?done', function(done_migrations) { + c.api('GET', '/migrations?pending', {}, function(pending_migrations) { + c.api('GET', '/migrations?done', {}, function(done_migrations) { pending_migrations = pending_migrations.migrations; done_migrations = done_migrations.migrations; @@ -302,66 +273,39 @@ c.view('tools/tools_migrations', { 'pending_migrations' : pending_migrations.reverse(), 'done_migrations' : done_migrations.reverse() + }, function() { + + // Configure button 'Run' + $('button[data-action="run"]').on("click", function() { + + var disclaimerAcks = $(".disclaimer-ack"); + for (var i = 0 ; i < disclaimerAcks.length ; i++) + { + if (! $(disclaimerAcks[i]).find("input:checked").val()) + { + // FIXME / TODO i18n + c.flash('fail', "Some of these migrations require you to acknowledge a disclaimer before running them."); + c.refresh(); + return; + } + }; + + c.api('POST', '/migrations/migrate?accept_disclaimer', {}, function() { c.refresh(); }); + }); + + // Configure buttons 'Skip' + $('button[data-action="skip"]').on("click", function() { + var migration_id = $(this).data("migration"); + c.confirm( + y18n.t('migrations'), + y18n.t('confirm_migrations_skip'), + function(){ + c.api('POST', '/migrations/migrate?skip&targets=' + migration_id, {}, function() { c.refresh() }); + } + ); + }); }); }); }); }); - - app.get('#/tools/migrations/run', function (c) { - var disclaimerAcks = $(".disclaimer-ack"); - var withAcceptDisclaimerFlag = false; - for (var i = 0 ; i < disclaimerAcks.length ; i++) - { - console.log($(disclaimerAcks[i]).find("input:checked").val()); - if (! $(disclaimerAcks[i]).find("input:checked").val()) - { - // FIXME / TODO i18n - c.flash('fail', "Some of these migrations require you to acknowledge a disclaimer before running them."); - c.redirect('#/tools/migrations'); - return; - } - else - { - withAcceptDisclaimerFlag = true; - } - }; - - // Not sure if necessary, but this distinction is to avoid accidentally - // triggering a migration with a disclaimer if one goes to the - // /tools/migrations/run page "directly" somehow ... - if (withAcceptDisclaimerFlag) - { - c.api('/migrations/migrate?accept_disclaimer', - function (data) { - store.clear('slide'); - c.redirect('#/tools/migrations'); - }, 'POST') - } - else - { - c.api('/migrations/migrate', - function (data) { - store.clear('slide'); - c.redirect('#/tools/migrations'); - }, 'POST') - } - }); - - app.get('#/tools/migrations/skip/:migration_id', function (c) { - c.confirm( - y18n.t('migrations'), - y18n.t('confirm_migrations_skip'), - function(){ - c.api('/migrations/migrate?skip&targets=' + c.params['migration_id'], function(data) { - store.clear('slide'); - c.redirect('#/tools/migrations'); - }, 'POST'); - }, - function(){ - store.clear('slide'); - c.redirect('#/tools/migrations'); - } - ); - }); - })(); diff --git a/src/js/yunohost/controllers/users.js b/src/js/yunohost/controllers/users.js index 655ff4c5..7da1bcb6 100644 --- a/src/js/yunohost/controllers/users.js +++ b/src/js/yunohost/controllers/users.js @@ -31,20 +31,20 @@ * @param.item Name of the user or the permission to add or remove * @param.group Name of the group affected * - * This function is built to be apply with params generated by the use of + * This function is built to be apply with params generated by the use of * HTML dataset attributes (e.g. link in the partial inline view "label" in group_list.ms) * * @return void **/ function updateGroup(model, params) { var type = params.type; - var operation = params.operation; + var action = params.action; var item = params.item; var groupname = params.group; var group = data.groups[groupname]; - var to = (operation == 'add')?group[type]:group[type + 'Inv']; - var from = (operation == 'add')?group[type+'Inv']:group[type]; - // Do nothing, if array of destination already contains the item + var to = (action == 'add')?group[type]:group[type + 'Inv']; + var from = (action == 'add')?group[type+'Inv']:group[type]; + // Do nothing, if array of destination already contains the item if (from.indexOf(item) === -1) return; // Hack to disable pacman loader if any @@ -52,22 +52,22 @@ $('#main').append(''); } $('div.loader').css('display', 'none'); - + // Update group var params = {}; var url; if (type == 'members') { url = '/users/groups/' + groupname; - params[operation] = [item]; + params[action] = [item]; } else { url = '/users/permissions/' + item; - params[operation] = [groupname]; + params[action] = [groupname]; } - c.api(url, function(data_update) { + c.api('PUT', url, params, function(data_update) { to.push(item); from.splice(from.indexOf(item), 1); updateView(data); - }, 'PUT', params); + }); } /** @@ -85,29 +85,41 @@ model.groups[group].members.sort(); model.groups[group].membersInv.sort(); } - - // Manual render, we don't use c.view to avoid scrollTop and other + + // Manual render, we don't use c.view to avoid scrollTop and other // uneeded behaviour var rendered = c.render('views/user/group_list.ms', model); rendered.swap(function () { // Add click event to get a nice "reactive" interface - jQuery(".group-update").on('click', function (e) { - updateGroup(model, jQuery(this)[0].dataset); + $("button[data-action='add'], button[data-action='remove']").on('click', function (e) { + updateGroup(model, $(this)[0].dataset); return false; }); - jQuery(".group-add-user").on('click', function (e) { - data.groups[$(this)[0].dataset.user].display = true; + $('button[data-action="add-user-specific-permission"]').on('click', function (e) { + data.groups[$(this).data("user")].display = true; updateView(data); return false; }); + $('button[data-action="delete-group"]').on('click', function (e) { + + var group = $(this).data("group"); + + c.confirm( + y18n.t('groups'), + $('
'+ y18n.t('confirm_delete', [group]) +'
'), + function() { + c.api('DELETE', '/users/groups/'+ group, {}, function(data) { c.refresh(); }); + } + ); + }); }); } app.get('#/groups', function (c) { - c.api('/users/groups?full&include_primary_groups', function(data_groups) { - c.api('/users', function(data_users) { - c.api('/users/permissions?short', function(data_permissions) { + c.api('GET', '/users/groups?full&include_primary_groups', {}, function(data_groups) { + c.api('GET', '/users', {}, function(data_users) { + c.api('GET', '/users/permissions?short', {}, function(data_permissions) { //var perms = data_permissions.permissions; var specific_perms = {}; var all_perms = data_permissions.permissions; @@ -129,7 +141,7 @@ // Declare all_users and visitors has special data_groups.groups['all_users'].special = true; data_groups.groups['visitors'].special = true; - + // Data given to the view with 2 functions to convert technical // permission id to display names data = { @@ -166,33 +178,9 @@ app.post('#/groups/create', function (c) { c.params['groupname'] = c.params['groupname'].replace(' ', '_').toLowerCase(); - c.api('/users/groups', function(data) { - c.redirect('#/groups'); - }, 'POST', c.params.toHash()); - }); - - app.get('#/groups/:group/delete', function (c) { - - var params = {}; - - // make confirm content - var confirmModalContent = $('
'+ y18n.t('confirm_delete', [c.params['group']]) +'
'); - - // display confirm modal - c.confirm( - y18n.t('groups'), - confirmModalContent, - function(){ - c.api('/users/groups/'+ c.params['group'], function(data) { - c.redirect('#/groups'); - }, 'DELETE', params); - }, - function(){ - //store.clear('slide'); - c.redirect('#/groups'); - } - ); - + c.api('POST', '/users/groups', c.params.toHash(), function(data) { + c.redirect_to('#/groups'); + }); }); /** @@ -202,14 +190,14 @@ // List existing users app.get('#/users', function (c) { - c.api('/users', function(data) { // http://api.yunohost.org/#!/user/user_list_get_3 + c.api('GET', '/users', {}, function(data) { c.view('user/user_list', data); }); }); // Create user form app.get('#/users/create', function (c) { - c.api('/domains', function(data) { // http://api.yunohost.org/#!/domain/domain_list_get_2 + c.api('GET', '/domains', {}, function(data) { // Password min length data.password_min_length = PASSWORD_MIN_LENGTH; @@ -230,7 +218,6 @@ if (c.params['password'] == c.params['confirmation']) { if (c.params['password'].length < PASSWORD_MIN_LENGTH) { c.flash('fail', y18n.t('password_too_short')); - store.clear('slide'); } else { // Force unit or disable quota @@ -242,28 +229,63 @@ // Compute email field c.params['mail'] = c.params['email'] + c.params['domain']; - c.api('/users', function(data) { // http://api.yunohost.org/#!/user/user_create_post_2 - c.redirect('#/users'); - }, 'POST', c.params.toHash()); + c.api('POST', '/users', c.params.toHash(), function(data) { + c.redirect_to('#/users'); + }); } } else { c.flash('fail', y18n.t('passwords_dont_match')); - store.clear('slide'); - //c.redirect('#/users/create'); } }); // Show user information app.get('#/users/:user', function (c) { - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_info_get_0 - c.view('user/user_info', data); + c.api('GET', '/users/'+ c.params['user'], {}, function(data) { + c.view('user/user_info', data, function() { + + // Configure delete button behavior + $('button[data-action="delete"]').on("click", function() { + var user = $(this).data("user"); + + var params = {}; + + // make confirm content + var purgeCheckbox = '
'; + var purgeAlertMessage = ''; + var confirmModalContent = $('
'+ y18n.t('confirm_delete', [user]) +'

'+ purgeCheckbox +'
'+ purgeAlertMessage +'
'); + + // display confirm modal + c.confirm( + y18n.t('users'), + confirmModalContent, + function(){ + c.api('DELETE', '/users/'+ user, params, function(data) { + c.redirect_to('#/users'); + }); + } + ); + + // toggle purge warning and parameter + confirmModalContent.find("input").click(function(){ + + if (confirmModalContent.find("input").is(':checked')) { + params.purge = ""; + confirmModalContent.find(".danger").show(); + } + else { + delete params.purge; + confirmModalContent.find(".danger").hide(); + }; + }); + }); + }); }); }); // Edit user form app.get('#/users/:user/edit', function (c) { - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_info_get_0 - c.api('/domains', function(dataDomains) { // http://api.yunohost.org/#!/domain/domain_list_get_2 + c.api('GET', '/users/'+ c.params['user'], {}, function(data) { + c.api('GET', '/domains', {}, function(dataDomains) { // Password min length data.password_min_length = PASSWORD_MIN_LENGTH; @@ -314,7 +336,7 @@ // Update user information app.put('#/users/:user', function (c) { // Get full user object - c.api('/users/'+ c.params['user'], function(user) { + c.api('GET', '/users/'+ c.params['user'], {}, function(user) { // Force unit or disable quota if (c.params['mailbox_quota']) { c.params['mailbox_quota'] += "M"; @@ -353,79 +375,32 @@ if ($.isEmptyObject(params)) { c.flash('fail', y18n.t('error_modify_something')); - store.clear('slide'); - c.redirect('#/users/'+ c.params['user'] + '/edit'); + c.redirect_to('#/users/'+ c.params['user'] + '/edit', {slide: false}); } else { if (params['password']) { if (params['password'] == params['confirmation']) { if (params['password'].length < PASSWORD_MIN_LENGTH) { c.flash('fail', y18n.t('password_too_short')); - store.clear('slide'); - c.redirect('#/users/'+ c.params['user'] + '/edit'); + c.redirect_to('#/users/'+ c.params['user'] + '/edit', {slide: false}); } else { params['change_password'] = params['password']; - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_update_put_1 - c.redirect('#/users/'+ c.params['user']); - }, 'PUT', params); + c.api('PUT', '/users/'+ c.params['user'], params, function(data) { + c.redirect_to('#/users/'+ c.params['user']); + }); } } else { c.flash('fail', y18n.t('passwords_dont_match')); - store.clear('slide'); - c.redirect('#/users/'+ c.params['user'] + '/edit'); + c.redirect_to('#/users/'+ c.params['user'] + '/edit', {slide: false}); } } else { - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_update_put_1 - c.redirect('#/users/'+ c.params['user']); - }, 'PUT', params); + c.api('PUT', '/users/'+ c.params['user'], params, function(data) { + c.redirect_to('#/users/'+ c.params['user']); + }); } } - }, 'GET'); - }); - - // Remove existing user - app.get('#/users/:user/delete', function (c) { - - var params = {}; - - // make confirm content - var purgeCheckbox = '
'; - var purgeAlertMessage = ''; - var confirmModalContent = $('
'+ y18n.t('confirm_delete', [c.params['user']]) +'

'+ purgeCheckbox +'
'+ purgeAlertMessage +'
'); - - // display confirm modal - c.confirm( - y18n.t('users'), - confirmModalContent, - function(){ - c.api('/users/'+ c.params['user'], function(data) { // http://api.yunohost.org/#!/user/user_delete_delete_4 - c.redirect('#/users'); - }, 'DELETE', params); - }, - function(){ - store.clear('slide'); - c.redirect('#/users/'+ c.params['user']); - } - ); - - // toggle purge warning and parameter - confirmModalContent.find("input").click(function(){ - - if (confirmModalContent.find("input").is(':checked')) { - params.purge = ""; - confirmModalContent.find(".warning").show(); - } - else { - delete params.purge; - confirmModalContent.find(".warning").hide(); - }; - }); - }); - - - })(); diff --git a/src/js/yunohost/events.js b/src/js/yunohost/events.js index 8b9abb4c..a6cde272 100644 --- a/src/js/yunohost/events.js +++ b/src/js/yunohost/events.js @@ -8,7 +8,7 @@ * */ app.bind('login', function(e, data) { - c.api('/users', function(data) { + c.api('GET', '/users', {}, function(data) { // Warn admin if no users are created. if (typeof data.users !== 'undefined' && data.users.length === 0) { c.flash('warning', y18n.t('warning_first_user')); @@ -69,13 +69,10 @@ c.flash('fail', y18n.t('error_retrieve_feed', [securityFeed])); }); - c.api("/diagnosis", function(data) { - versions = data.packages; - $('#yunohost-version').html(y18n.t('footer_version', [versions.yunohost.version, versions.yunohost.repo])); - if (data.security["CVE-2017-5754"].vulnerable) { - c.flash('danger', y18n.t('meltdown')); - } - $('div.loader').remove(); + c.api("GET", "/diagnosis/show?full", {}, function(data) { + basesystem = data.reports.filter(function(r) { return r.id == "basesystem"; })[0]; + version_info = basesystem.items.filter(function(i) { return (i.meta && i.meta.test && i.meta.test == "ynh_versions"); })[0]; + $('#yunohost-version').html(y18n.t('footer_version', [version_info.data.main_version, version_info.data.repo])); }); }); }); diff --git a/src/js/yunohost/filters.js b/src/js/yunohost/filters.js index 0b793580..c35a9450 100644 --- a/src/js/yunohost/filters.js +++ b/src/js/yunohost/filters.js @@ -12,7 +12,7 @@ function prefetchDomains(req) { // Preload domains list. req.params.domains = []; - req.api('/domains', function(data) { + req.api('GET', '/domains', {}, function(data) { req.params.domains = data.domains; }); } @@ -20,7 +20,7 @@ function prefetchUsers(req){ // Preload users lists. req.params.users = []; - req.api('/users', function(data) { + req.api('GET', '/users', {}, function(data) { req.params.users = data.users; }); } diff --git a/src/js/yunohost/helpers.js b/src/js/yunohost/helpers.js index 44ea77e6..59e70ed0 100644 --- a/src/js/yunohost/helpers.js +++ b/src/js/yunohost/helpers.js @@ -3,20 +3,64 @@ var app = Sammy.apps['#main']; var store = app.store; + // The logic used to temporily disable transition is from + // https://stackoverflow.com/a/16575811 + function whichTransitionEvent(){ + var t; + var el = document.createElement('fakeelement'); + var transitions = { + 'transition':'transitionend', + 'OTransition':'oTransitionEnd', + 'MozTransition':'transitionend', + 'WebkitTransition':'webkitTransitionEnd' + } + + for(t in transitions){ + if( el.style[t] !== undefined ){ + return transitions[t]; + } + } + }; + var transitionEvent = whichTransitionEvent(); + + function resetSliders() + { + // Disable transition effects + $('#slider-container').addClass('notransition'); + // Delete the left/right temporary stuff only used during animation + $('#slideTo').css('display', 'none'); + $('#slideTo').html(""); + $('#slideBack').css('display', 'none'); + $('#slideBack').html(""); + // Set the margin-left back to 0 + $('#slider-container').css('margin-left', '0'); + // c.f. the stackoverflow thread + $('#slider-container')[0].offsetHeight; + // Remove the binding to this event handler for next times + // Re-enable transition effects + $('#slider-container').removeClass('notransition'); + } + /** * Helpers * */ app.helpers({ - // Serialize an object - serialize : function(obj) { - var str = []; - for(var p in obj) - if (obj.hasOwnProperty(p)) { - str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); + // + // Pacman loader management + // + + showLoader: function() { + app.loaded = false; // Not sure if that's really useful ... this is from old code with no explanation what it really does ... + if ($('div.loader').length === 0) { + $('#main').append('
'); } - return str.join("&"); + }, + + hideLoader: function() { + app.loaded = true; // Not sure if that's really useful ... this is from old code with no explanation what it really does ... + $('div.loader').remove(); }, // Flash helper to diplay instant notifications @@ -85,7 +129,7 @@ }, // API call - api: function(uri, callback, method, data, websocket, callbackOnFailure) { + api: function(method, uri, data, callback, callbackOnFailure, websocket) { c = this; method = typeof method !== 'undefined' ? method : 'GET'; @@ -93,96 +137,69 @@ if (window.navigator && window.navigator.language && (typeof data.locale === 'undefined')) { data.locale = y18n.locale || window.navigator.language.substr(0, 2); } - app.loaded = false; - if ($('div.loader').length === 0) { - $('#main').append('
'); - } + + c.showLoader(); + call = function(uri, callback, method, data, callbackOnFailure) { - var args = data; - // TODO: change this code - if (uri === '/postinstall') { - var post_installing = false; - setInterval(function () { - post_installing = true; - }, 1500); - } - + // Define default callback for failures if (typeof callbackOnFailure !== 'function') { callbackOnFailure = function(xhr) { - // Postinstall is a custom case, we have to wait that - // operation is done before doing anything - if ((uri === '/postinstall') && (post_installing)) { - interval = window.location.hostname === args.domain ? 20000 : 5000; - checkInstall = setInterval(function () { - c.checkInstall(function(isInstalled) { - if (isInstalled || typeof isInstalled === 'undefined') { - c.flash('success', y18n.t('installation_complete')); - clearInterval(checkInstall); - window.location.href = 'https://'+ window.location.hostname +'/yunohost/admin/'; - } - }); - }, interval); + if (xhr.status == 200) { + // Fail with 200, WTF + callback({}); } - // Regular errors + // Unauthorized or wrong password + else if (xhr.status == 401) { + if (uri === '/login') { + c.flash('fail', y18n.t('wrong_password')); + } else { + c.flash('fail', y18n.t('unauthorized')); + c.redirect('#/login'); + } + } + // 500 + else if (xhr.status == 500) { + try { + error_log = JSON.parse(xhr.responseText); + error_log.route = error_log.route.join(' ') + '\n'; + error_log.arguments = JSON.stringify(error_log.arguments); + } + catch (e) + { + error_log = {}; + error_log.route = "Failed to parse route"; + error_log.arguments = "Failed to parse arguments"; + error_log.traceback = xhr.responseText; + } + c.flash('fail', y18n.t('internal_exception', [error_log.route, error_log.arguments, error_log.traceback])); + } + // 502 Bad gateway means API is down + else if (xhr.status == 502) { + c.flash('fail', y18n.t('api_not_responding')); + } + // More verbose error messages first + else if (typeof xhr.responseText !== 'undefined') { + c.flash('fail', xhr.responseText); + } + // 0 mean "the connexion has been closed" apparently + else if (xhr.status == 0) { + var errorMessage = xhr.status+' '+xhr.statusText; + c.flash('fail', y18n.t('error_connection_interrupted', [errorMessage])); + console.log(xhr); + } + // Return HTTP error code at least else { - if (xhr.status == 200) { - // Fail with 200, WTF - callback({}); - } - // Unauthorized or wrong password - else if (xhr.status == 401) { - if (uri === '/login') { - c.flash('fail', y18n.t('wrong_password')); - } else { - c.flash('fail', y18n.t('unauthorized')); - c.redirect('#/login'); - } - } - // 500 - else if (xhr.status == 500) { - try { - error_log = JSON.parse(xhr.responseText); - error_log.route = error_log.route.join(' ') + '\n'; - error_log.arguments = JSON.stringify(error_log.arguments); - } - catch (e) - { - error_log = {}; - error_log.route = "Failed to parse route"; - error_log.arguments = "Failed to parse arguments"; - error_log.traceback = xhr.responseText; - } - c.flash('fail', y18n.t('internal_exception', [error_log.route, error_log.arguments, error_log.traceback])); - } - // 502 Bad gateway means API is down - else if (xhr.status == 502) { - c.flash('fail', y18n.t('api_not_responding')); - } - // More verbose error messages first - else if (typeof xhr.responseText !== 'undefined') { - c.flash('fail', xhr.responseText); - } - // 0 mean "the connexion has been closed" apparently - else if (xhr.status == 0) { - var errorMessage = xhr.status+' '+xhr.statusText; - c.flash('fail', y18n.t('error_connection_interrupted', [errorMessage])); - console.log(xhr); - } - // Return HTTP error code at least - else { - var errorMessage = xhr.status+' '+xhr.statusText; - c.flash('fail', y18n.t('error_server_unexpected', [errorMessage])); - console.log(xhr); - } - - // Remove loader if any - $('div.loader').remove(); - - // Force scrollTop on page load - $('html, body').scrollTop(0); - store.clear('slide'); + var errorMessage = xhr.status+' '+xhr.statusText; + c.flash('fail', y18n.t('error_server_unexpected', [errorMessage])); + console.log(xhr); } + + c.hideLoader(); + + // Force scrollTop on page load + $('html, body').scrollTop(0); + store.clear('slide'); }; } @@ -237,78 +254,17 @@ }, - // Render view (cross-browser) - view: function (view, data, callback, enableSlide) { + + // Ask confirmation to the user through the modal window + confirm: function(title, content, confirmCallback, cancelCallback) { c = this; - // Default - callback = typeof callback !== 'undefined' ? callback : function() {}; - enableSlide = (typeof enableSlide !== 'undefined') ? enableSlide : true; // Change to false to disable animation - - app.loaded = true; - - // Hide loader and modal - $('div.loader').remove(); - $('#modal').modal('hide'); - - // Render content - var rendered = this.render('views/'+ view +'.ms', data); - - // Update content helper - var leSwap = function() { - rendered.swap(function() { - // Slide direction - if (enableSlide) { - $('.slide, .btn-breadcrumb a:not(:last-child)').on('click', function() { - $(this).addClass('active'); - if ($(this).hasClass('back') || $(this).parent('.btn-breadcrumb').length) { - store.set('slide', 'back'); - } else { - store.set('slide', 'to'); - } - }); - } - - // Paste
 helper
-                    c.prePaste();
-
-                    // Run callback
-                    callback();
-
-                    // Force scrollTop on page load
-                    $('html, body').scrollTop(0);
-                });
-            };
-
-            // Slide back effect
-            if (enableSlide && store.get('slide') == 'back') {
-                store.clear('slide');
-                $('#slideBack').css('display', 'none');
-                $('#slider-container').css('margin-left', '-100%');
-                $('#slideTo').show().html($('#main').html());
-                leSwap();
-                $('#slider-container').css('margin-left', '0px');
-            }
-            // Slide to effect
-            else if (enableSlide && store.get('slide') == 'to') {
-                store.clear('slide');
-                $('#slideTo').css('display', 'none');
-                $('#slider-container').css('margin-left', '0px');
-                $('#slideBack').show().html($('#main').html());
-                leSwap();
-                $('#slider-container').css('margin-left', '-100%');
-            }
-            // No slideing effect
-            else {
-                leSwap();
-            }
-        },
-
-        confirm: function(title, content, confirmCallback, cancelCallback) {
             // Default callbacks
             confirmCallback = typeof confirmCallback !== 'undefined' ? confirmCallback : function() {};
             cancelCallback = typeof cancelCallback !== 'undefined' ? cancelCallback : function() {};
 
+            c.hideLoader();
+
             // Get modal element
             var box = $('#modal');
 
@@ -335,12 +291,10 @@
 
                     $('#modal footer button').unbind( "click" );
                     // Reset & Hide modal
-                    box
-                        .removeClass('no-title')
-                        .modal('hide');
+                    box.removeClass('no-title').modal('hide');
 
                     // Do corresponding callback
-                    if ($(this).data('action') == 'confirm') {
+                    if ($(this).data('modal-action') == 'confirm') {
                         confirmCallback();
                     }
                     else {
@@ -352,19 +306,165 @@
             return box.modal('show');
         },
 
-        selectAllOrNone: function () {
-          // Remove active style from buttons
-          $(".select_all-none input").click(function(){ $(this).toggleClass("active"); });
-          // Select all checkbox in this panel
-          $(".select_all").click(function(){
-            $(this).parents(".panel").children(".list-group").find("input").prop("checked", true);
-          });
-          // Deselect all checkbox in this panel
-          $(".select_none").click(function(){
-            $(this).parents(".panel").children(".list-group").find("input").prop("checked", false);
-          });
+
+        // Render view (cross-browser)
+        view: function (view, data, callback) {
+            c = this;
+
+            // Default
+            callback = typeof callback !== 'undefined' ? callback : function() {};
+
+            // Hide loader and modal
+            c.hideLoader();
+            $('#modal').modal('hide');
+
+            // Render content
+            var rendered = this.render('views/'+ view +'.ms', data);
+
+            // Update content helper
+            var leSwap = function() {
+                rendered.swap(function() {
+                    // Clicking on those kind of CSS elements will trigger a
+                    // slide effect i.e. the next view rendering will have
+                    // store.get('slide') set to 'back' or 'to'
+                    $('.slide, .btn-breadcrumb a:not(:last-child)').on('click', function() {
+                        $(this).addClass('active');
+                        if ($(this).hasClass('back') || $(this).parent('.btn-breadcrumb').length) {
+                            store.set('slide', 'back');
+                        } else {
+                            store.set('slide', 'to');
+                        }
+                    });
+
+                    // Paste 
 helper
+                    c.prePaste();
+
+                    // Run callback
+                    callback();
+
+                    // Force scrollTop on page load
+                    $('html, body').scrollTop(0);
+                });
+            };
+
+            // Slide back effect
+            if (store.get('slide') == 'back') {
+
+                store.clear('slide');
+                // Disable transition while we tweak CSS
+                $('#slider-container').addClass('notransition');
+                // "Delete" the left part of the slider
+                $('#slideBack').css('display', 'none');
+
+                // Push the slider to the left
+                $('#slider-container').css('margin-left', '-100%');
+                // slideTo is the right part, and should contain the old view,
+                // so we copypasta what's in the "center" slider (#main)
+                $('#slideTo').show().html($('#main').html());
+                // leSwap will put the new view in the "center" slider (#main)
+                leSwap();
+
+                // So now things look like:
+                //                          |                 |
+                //                          |   the screen    |
+                //                          |                 |
+                //
+                //       .     #main        .    #slideTo     .
+                //       .  the new view    .  the old view   .
+                //       ^                          ^
+                //  margin-left: -100%             currently shown
+                //
+                //            =====>>>  sliiiiide  =====>>>
+
+                // Re-add transition effect
+                $('#slider-container').removeClass('notransition');
+
+                // add the transition event to detect the end of the transition effect
+                transitionEvent
+                    && $("#slider-container").off(transitionEvent)
+                    && $("#slider-container").on(transitionEvent, resetSliders);
+
+                // And actually play the transition effect that will move the container from left to right
+                $('#slider-container').css('margin-left', '0px');
+            }
+            // Slide to effect
+            else if (store.get('slide') == 'to') {
+
+                // Disable transition while we tweak CSS
+                $('#slider-container').addClass('notransition');
+                // "Delete" the right part of the slider
+                $('#slideTo').css('display', 'none');
+                // Push the slider to the right
+                $('#slider-container').css('margin-left', '0px');
+                // slideBack should contain the old view,
+                // so we copypasta what's in the "center" slider (#main)
+                $('#slideBack').show().html($('#main').html());
+                leSwap();
+
+                // So now things look like:
+                //
+                //                    |                 |
+                //                    |   the screen    |
+                //                    |                 |
+                //
+                //      .             .   #slideBack    .     #main      .
+                //      .             .  the old view   .  the new view  .
+                //      ^             ^        ^
+                //   margin-left: -100%      currently shown
+                //
+                //               <<<===== sliiiiide <<<=======
+
+
+                // Re-add transition effect
+                $('#slider-container').removeClass('notransition');
+
+                // add the transition event to detect the end of the transition effect
+                var transitionEvent = whichTransitionEvent();
+                transitionEvent
+                    && $("#slider-container").off(transitionEvent)
+                    && $("#slider-container").on(transitionEvent, resetSliders);
+
+                // And actually play the transition effect that will move the container from right to left
+                $('#slider-container').css('margin-left', '-100%');
+            }
+            // No slideing effect
+            else {
+                leSwap();
+            }
         },
 
+        redirect_to: function(destination, options) {
+            c = this;
+
+            options = options !== undefined ? options : {};
+
+            // If destination if the same as current url,
+            // we don't want to display the slide animation
+            // (or if the code explicitly state to disable slide animation)
+            if ((c.path.split("#")[1] == destination.split("#")[1]) || (options.slide == false))
+            {
+                store.clear('slide');
+            }
+
+            // This is a copy-pasta of some of the redirect/refresh code of
+            // sammy.js because for some reason calling the original
+            // redirect/refresh function in some context does not work >.>
+            // (e.g. if you're already on the page)
+            c.trigger('redirect', {to: destination});
+            c.app.last_location = c.path;
+            c.app.setLocation(destination);
+            c.app.trigger('location-changed');
+        },
+
+        refresh: function() {
+            c = this;
+            c.redirect_to(c.path, {slide: false});
+        },
+
+        //
+        // Array / object helpers
+        //
+
         arraySortById: function(arr) {
             arr.sort(function(a, b){
                 if (a.id > b.id) {
@@ -385,74 +485,20 @@
             });
         },
 
-        groupHooks: function(hooks, raw_infos){
-            var data = {};
-            var rules = [
-                {
-                    id:'configuration',
-                    isIn:function (hook) {
-                        return hook.indexOf('conf_')==0
-                    }
-                }
-            ];
-
-            $.each(hooks, function(i, hook) {
-                var group_id=hook;
-                var hook_size=(raw_infos && raw_infos[hook] && raw_infos[hook].size)?raw_infos[hook].size:0;
-                $.each(rules, function(i, rule) {
-                    if (rule.isIn(hook)) {
-                        group_id = 'adminjs_group_'+rule.id;
-                        return false;
-                    }
-                });
-
-                if(group_id in data) {
-                    data[group_id] = {
-                        name:y18n.t('hook_'+group_id),
-                        value:data[group_id].value+','+hook,
-                        description:data[group_id].description+', '+y18n.t('hook_'+hook),
-                        size:data[group_id].size + hook_size
-                    };
-                }
-                else {
-                    data[group_id] = {
-                        name:y18n.t('hook_'+group_id),
-                        value:hook,
-                        description:(group_id==hook)?y18n.t('hook_'+hook+'_desc'):y18n.t('hook_'+hook),
-                        size:hook_size
-                    };
-                }
-            });
-            return data;
+        // Serialize an object
+        serialize : function(obj) {
+          var str = [];
+          for(var p in obj)
+            if (obj.hasOwnProperty(p)) {
+              str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
+            }
+          return str.join("&");
         },
 
-        ungroupHooks: function(system_parts,apps) {
-            var data = {};
-            data['apps'] = apps || [];
-            data['system'] = system_parts || [];
 
-            if (data['system'].constructor !== Array) {
-                data['system'] = [data['system']];
-            }
-            if (data['apps'].constructor !== Array) {
-                data['apps'] = [data['apps']];
-            }
-
-            // Some hook value contains multiple hooks separated by commas
-            var split_hooks = [];
-            $.each(data['system'], function(i, hook) {
-                split_hooks = split_hooks.concat(hook.split(','));
-            });
-            data['system'] = split_hooks;
-
-            if (data['system'].length == 0) {
-                delete data['system'];
-	    }
-            if (data['apps'].length == 0) {
-                delete data['apps'];
-	    }
-            return data;
-        },
+        //
+        // Misc helpers used in views etc..
+        //
 
         // Paste 
         prePaste: function() {
@@ -461,8 +507,7 @@
                 // Get paste content element
                 var preElement = $($(this).data('paste-content'));
 
-                // Add pacman loader
-                $('#main').append('
'); + c.showLoader(); // Send to paste.yunohost.org $.ajax({ @@ -477,11 +522,23 @@ c.flash('fail', y18n.t('paste_error')); }) .always(function(){ - // Remove pacman - $('div.loader').remove(); + c.hideLoader(); }); }); + }, + + force_redirect: function(to) { + c = this; + // This is a copy-pasta of some of the redirect/refresh code of + // sammy.js because for some reason calling the origina + // redirect/refresh function in some context does not work >.> + // (e.g. if you're already on the page) + c.trigger('redirect', {to: to}); + c.app.last_location = c.path; + c.app.setLocation(to); + c.app.trigger('location-changed'); } + }); })(); diff --git a/src/js/yunohost/main.js b/src/js/yunohost/main.js index 4d6ef4e4..72615a31 100644 --- a/src/js/yunohost/main.js +++ b/src/js/yunohost/main.js @@ -181,9 +181,10 @@ sam.store.set('url', window.location.hostname + '/yunohost/api'); if (sam.store.get('connected')) { - this.api('/diagnosis', function(diagnosis) { - versions = diagnosis.packages; - $('#yunohost-version').html(y18n.t('footer_version', [versions.yunohost.version, versions.yunohost.repo])); + this.api('GET', '/diagnosis/show?full', {}, function(data) { + basesystem = data.reports.filter(function(r) { return r.id == "basesystem"; })[0]; + version_info = basesystem.items.filter(function(i) { return (i.meta && i.meta.test && i.meta.test == "ynh_versions"); })[0]; + $('#yunohost-version').html(y18n.t('footer_version', [version_info.data.main_version, version_info.data.repo])); }); } diff --git a/src/locales/ca.json b/src/locales/ca.json index e9d0a8de..edd9af72 100644 --- a/src/locales/ca.json +++ b/src/locales/ca.json @@ -18,7 +18,7 @@ "app_change_url": "Canvia l'URL", "app_debug_no_logs": "Els registres de l'aplicació no estan disponibles", "app_debug_tab": "Mostra la informació de depuració", - "app_info_access_desc": "Administrar l'accés d'usuaris. Usuaris permesos: %s", + "app_info_access_desc": "Grups / usuaris autoritzats actualment a accedir a aquesta aplicació:", "app_info_changelabel_desc": "Canvia l'etiqueta de l'aplicació al portal.", "app_info_debug_desc": "Mostrar la informació de depuració per aquesta aplicació.", "app_info_default_desc": "Redirigeix l'arrel del domini a aquesta aplicació (%s).", @@ -437,5 +437,20 @@ "request_help": "ajuda necessària", "request_help_details": "El desenvolupador actual necessita ajuda per mantenir aquesta aplicació. Podeu contribuir-hi!", "advanced": "Avançat", - "from_to": "de %s fins a %s" + "from_to": "de %s fins a %s", + "group": "Grup", + "group_name": "Nom del grup", + "group_all_users": "Tots els usuaris", + "group_visitors": "Visitants", + "group_format_name_help": "Podeu utilitzar caràcters alfanumèrics i l'espai", + "group_add_member": "Afegir usuari", + "group_explain_all_users": "Aquest és un grup especial que conté tots els comptes d'usuari del servidor", + "group_add_permission": "Afegir permís", + "group_new": "Nou grup", + "group_explain_visitors": "Aquest és un grup especial que representa els visitants anònims", + "group_specific_permissions": "Permisos específics de l'usuari", + "groups_and_permissions": "Grups i permisos", + "groups_and_permissions_manage": "Gestionar grups i usuaris", + "permissions": "Permisos", + "nobody": "Ningú" } diff --git a/src/locales/en.json b/src/locales/en.json index 4ac42be3..61a5c96e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -9,8 +9,6 @@ "api_not_responding": "The YunoHost API is not responding. Maybe 'yunohost-api' is down or got restarted?", "app_change_label": "Change Label", "app_change_url": "Change URL", - "app_debug_no_logs": "Application's logs are not available", - "app_debug_tab": "Display debug information", "app_info_access_desc": "Groups / users currently allowed to access this app:", "app_info_changelabel_desc": "Change app label in the portal.", "app_info_debug_desc": "Display debugging information for this application.", @@ -36,7 +34,6 @@ "application": "Application", "applications": "Applications", "archive_empty": "Empty archive", - "available": "Available", "available_apps": "Available apps", "backup": "Backup", "backup_action": "Backup", @@ -52,12 +49,9 @@ "backup_type": "Type", "backups_no": "No backup", "begin": "Begin", - "bit_rate": "Bit rate", "both": "Both", "cancel": "Cancel", "check": "Check", - "check_mx": "MX record", - "check_stmp": "port 25 access", "close": "Close", "confirm_app_change_url": "Are you sure you want to change the app access URL?", "confirm_app_default": "Are you sure you want to make this app default?", @@ -86,24 +80,19 @@ "confirm_reboot_action_shutdown": "Are you sure you want to shutdown your server?", "connection": "Connection", "copy": "Copy", - "count_min": "%s min", - "cpu_load": "CPU Load", "created_at": "Created at", - "cumulative_usage": "Cumulative usage", "current_maintainer_title": "Current maintainer of this package", "custom_app_install": "Install custom app", "custom_app_url_only_github": "Currently only from GitHub", "default": "Default", "delete": "Delete", "description": "Description", + "details": "Details", "domain_dns_conf_is_just_a_recommendation": "This page shows you the *recommended* configuration. It does *not* configure the DNS for you. It is your responsability to configure your DNS zone in your DNS registrar according to this recommendation.", "diagnosis": "Diagnosis", - "diagnosis_hide_private": "Show diagnostic information without private data", - "diagnosis_view_private": "Show diagnostic information including private data", - "diagnosis_with_private": "Diagnosis with private data", + "diagnosis_experimental_disclaimer": "Be aware that the diagnosis feature is still experimental and being polished, and it may not be fully reliable.", "disable": "Disable", "disabled": "Disabled", - "disk": "Disk", "dns": "DNS", "domain": "Domain", "domain_add": "Add domain", @@ -126,21 +115,20 @@ "download": "Download", "enable": "Enable", "enabled": "Enabled", + "errors": "%s errors", "error_modify_something": "You should modify something", "error_retrieve_feed": "Could not retrieve feed: %s. You might have a plugin prevent your browser from performing this request (or the website is down).", "error_select_domain": "You should indicate a domain", "error_server": "Server error", "error_server_unexpected": "Unexpected server error (%s)", "error_connection_interrupted": "The server closed the connection instead of answering it. Has nginx or the yunohost-api been restarted or stoppted for some reason? (Error code/message: %s)", + "everything_good": "Everything good!", "experimental_warning": "Warning: this feature is experimental and not consider stable, you shouldn't be using it except if you know what you are doing.", - "filesystem": "Filesystem", "firewall": "Firewall", "footer_version": "Powered by YunoHost %s (%s).", "form_input_example": "Example: %s", "free": "Free", "from_to": "from %s to %s", - "fs_type": "FS Type", - "gateway": "Gateway: ", "good_practices_about_admin_password": "You are now about to define a new admin password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", "group": "Group", @@ -173,8 +161,9 @@ "hook_data_home_desc": "User data located in /home/USER", "hook_data_mail": "Mail", "hook_data_mail_desc": "Raw emails stored on the server", - "hostname": "Hostname", "id": "ID", + "ignore": "Ignore", + "ignored": "%s ignored", "inactive": "Inactive", "infos": "Info", "install": "Install", @@ -184,18 +173,16 @@ "installed": "Installed", "installed_apps": "Installed apps", "installing": "Installing", - "interface": "Interface", "internal_exception": "Yunohost encountered an internal error:/
Really sorry about that.
You should look for help on the forum or the chat to fix the situation, or report the bug on the bugtracker.

The following information might be useful for the person helping you:

Action

%s%s

Traceback

%s
", - "io": "I/O", "ipv4": "IPv4", "ipv6": "IPv6", "label": "Label", "label_for_manifestname": "Label for %s", + "last_ran": "Last time ran:", "level": "level", "license": "License", "loading": "Loading …", "local_archives": "Local archives", - "local_ip": "Local IP", "log": "Log", "logged_in": "Logged in", "logged_out": "Logged out", @@ -208,7 +195,6 @@ "manage_apps": "Manage apps", "manage_domains": "Manage domains", "manage_users": "Manage users", - "memory": "Memory", "menu": "Menu", "migrations": "Migrations", "migrations_pending": "Pending migrations", @@ -216,14 +202,9 @@ "migrations_no_pending": "No pending migrations", "migrations_no_done": "No previous migrations", "mode": "Mode", - "monitoring": "Monitoring", - "monitoring_check_glances": "Check glances service status.", - "monitoring_disabled": "Monitoring is not enabled.", - "mount_point": "Mount point", "multi_instance": "Multi instance", "myserver": "myserver", "myserver_org": "myserver.org", - "network": "Network", "next": "Next", "no": "No", "no_installed_apps": "No installed apps.", @@ -238,7 +219,6 @@ "operations": "Operations", "orphaned": "not maintained", "orphaned_details": "This app is not maintained anymore. It may still be working but won't receive any upgrade. Feel free to come and revive it!", - "os": "OS", "password": "Password", "password_confirmation": "Password confirmation", "password_description": "Password must be at least %s characters long.", @@ -247,10 +227,6 @@ "passwords_dont_match": "Passwords don't match", "passwords_too_short": "Password is too short", "path": "Path", - "diagnosis": "Diagnosis", - "diagnosis_with_private": "Diagnosis with private data", - "diagnosis_view_private": "Show diagnosis with private data", - "diagnosis_hide_private": "Show diagnosis without private data", "logs": "Logs", "logs_operation": "Operations made on system with YunoHost", "logs_history": "History of command run on system", @@ -278,20 +254,14 @@ "postinstall_intro_3": "You can obtain more information by visiting the appropriate documentation page", "postinstall_password": "This password will be used to manage everything on your server. Take the time to choose it wisely.", "previous": "Previous", - "process": "Process", "protocol": "Protocol", - "public_ip": "Public IP: ", - "ram": "RAM", - "read": "Read", "read_more": "Read more", - "reception": "Reception", "request_adoption": "waiting adoption", "request_adoption_details": "The current maintainer would like to stop maintaining this app. Feel free to propose yourself as the new maintainer!", "request_help": "need help", "request_help_details": "The current maintainer would like some help with the maintainance of this app. Feel free to come contribute on it!", "restore": "Restore", "run": "Run", - "running": "Running", "save": "Save", "search_for_apps": "Search for apps...", "select_all": "Select all", @@ -312,7 +282,6 @@ "storage_create": "Add remote storage", "storages_new": "New remote storage", "storages_no": "No storages.", - "swap": "Swap", "system": "System", "system_apps": "Apps", "system_apps_nothing": "There are no apps to upgrade.", @@ -324,7 +293,6 @@ "system_upgrade_all_applications_btn": "Upgrade all applications", "system_upgrade_all_packages_btn": "Upgrade all packages", "tcp": "TCP", - "time_since_update": "Time since update: ", "tools": "Tools", "tools_adminpw": "Change administration password", "tools_adminpw_confirm_placeholder": "Confirm the new password", @@ -346,10 +314,9 @@ "tools_shutdown_done": "Shutting down...", "tools_shuttingdown": "Your server is powering off. As long as your server is off, you won't be able to use the web administration.", "tools_shutdown_reboot": "Shutdown/Reboot", - "total": "Total", - "transmission": "Transmission", "udp": "UDP", "unauthorized": "Unauthorized", + "unignore": "Unignore", "uninstall": "Uninstall", "unknown_action": "Unknown action %s", "unknown_argument": "Unknown argument: %s", @@ -359,10 +326,7 @@ "upnp": "UPnP", "upnp_disabled": "UPnP is disabled.", "upnp_enabled": "UPnP is enabled.", - "uptime": "Uptime", "url": "URL", - "usage": "Usage", - "used": "Used", "user_email": "Email", "user_emailaliases": "Mail aliases", "user_emailforward": "Mail forward", @@ -378,10 +342,9 @@ "users_list": "User list", "users_new": "New user", "users_no": "No users.", - "versions": "Versions", "version": "Version", + "warnings": "%s warnings", "warning_first_user": "You probably need to create a user first.", - "write": "Write", "wrong_password": "Wrong password", "yes": "Yes", "certificate_alert_not_valid": "CRITICAL: Current certificate is not valid! HTTPS won't work at all!", @@ -413,6 +376,6 @@ "revert_to_selfsigned_cert_message": "If you really want to, you can reinstall a self-signed certificate. (Not recommended)", "revert_to_selfsigned_cert": "Revert to a self-signed certificate", "name": "Name", - "purge_user_data_checkbox": "Purge %s's data? (This will remove the content of it's home and mail directories.)", + "purge_user_data_checkbox": "Purge %s's data? (This will remove the content of its home and mail directories.)", "purge_user_data_warning": "Purging user's data is not reversible. Be sure you know what you're doing!" } diff --git a/src/locales/eo.json b/src/locales/eo.json index 98d15947..9457a628 100644 --- a/src/locales/eo.json +++ b/src/locales/eo.json @@ -124,7 +124,7 @@ "domain_dns_config": "Agordo DNS", "cpu_load": "CPU Ŝarĝo", "form_input_example": "Ekzemplo: %s", - "app_info_access_desc": "Administri la aliron de uzanto. Permesitaj uzantoj : %s", + "app_info_access_desc": "Grupoj/uzantoj nuntempe rajtas aliri ĉi tiun programon:", "diagnosis_with_private": "Diagnozo kun privataj datumoj", "install_name": "Instalu %s", "domain_default": "Defaŭlta domajno", @@ -425,5 +425,20 @@ "name": "Nomo", "udp": "UDP", "swap": "Interŝanĝu", - "user_interface_link": "Uzantinterfaco" + "user_interface_link": "Uzantinterfaco", + "group_format_name_help": "Vi povas uzi alfa-numerikajn signojn kaj spacon", + "group_add_member": "Aldonu uzanton", + "group": "Grupo", + "group_name": "Grupo nomo", + "group_all_users": "Ĉiuj uzantoj", + "groups_and_permissions": "Grupoj kaj permesoj", + "groups_and_permissions_manage": "Administri grupojn kaj permesojn", + "group_visitors": "Vizitantoj", + "group_explain_all_users": "Jen speciala grupo enhavanta ĉiujn uzantajn kontojn en la servilo", + "group_add_permission": "Aldonu permeson", + "group_new": "Nova grupo", + "group_explain_visitors": "Ĉi tio estas speciala grupo reprezentanta anonimajn vizitantojn", + "group_specific_permissions": "Uzaj specifaj permesoj", + "permissions": "Permesoj", + "nobody": "Neniu" } diff --git a/src/locales/es.json b/src/locales/es.json index 99a4f2cb..e543f4b0 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -14,7 +14,7 @@ "app_access_title": "Acceso %s 4", "app_debug_no_logs": "Los registros de la aplicación no están disponibles", "app_debug_tab": "Mostrar información de depuración", - "app_info_access_desc": "Administrar el acceso de usuarios. Usuarios permitidos: %s 5", + "app_info_access_desc": "Grupos / usuarios actualmente autorizados a acceder a esta aplicación:", "app_info_debug_desc": "Mostrar información de depuración para esta aplicación.", "app_info_default_desc": "Redirigir la raíz del dominio para esta aplicación (%s 6).", "app_info_uninstall_desc": "Eliminar a esta aplicación.", @@ -440,5 +440,20 @@ "appslists_confirm_remove": "¿Seguro que quiere eliminar esta lista de aplicaciones?", "level": "nivel", "system_upgrade_all_applications_btn": "Actualizar todas las aplicaciones", - "select_all": "Seleccionar todo" + "select_all": "Seleccionar todo", + "group_explain_all_users": "Este es un grupo especial que contiene todas las cuentas de usuarios en el servidor", + "group": "Grupo", + "group_name": "Nombre del grupo", + "group_all_users": "Todos los usuarios", + "group_visitors": "Visitantes", + "group_format_name_help": "Puede usar caracteres alfanuméricos y espacio", + "group_add_member": "Añadir un usuario", + "group_add_permission": "Añadir permiso", + "group_new": "Nuevo grupo", + "group_explain_visitors": "Este es un grupo especial que representa a los visitantes anónimos", + "group_specific_permissions": "Permisos específicos de usuario", + "groups_and_permissions": "Grupos y permisos", + "groups_and_permissions_manage": "Gestionar grupos y permisos", + "permissions": "Permisos", + "nobody": "Nadie" } diff --git a/src/locales/eu.json b/src/locales/eu.json index 0967ef42..74824cde 100644 --- a/src/locales/eu.json +++ b/src/locales/eu.json @@ -1 +1,6 @@ -{} +{ + "password": "Pasahitza", + "ok": "Ados", + "cancel": "Utzi", + "logged_out": "Saioa amaitu" +} diff --git a/src/locales/fr.json b/src/locales/fr.json index 3b0964ba..fb4550c4 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -14,7 +14,7 @@ "app_access_title": "Accès à %s", "app_debug_no_logs": "Les journaux de cette application ne sont pas disponibles", "app_debug_tab": "Afficher les informations de débogage", - "app_info_access_desc": "Gestion des droits d'accès. Utilisateurs autorisés : %s", + "app_info_access_desc": "Groupes / utilisateurs actuellement autorisés à accéder à cette application :", "app_info_debug_desc": "Afficher les informations de débogage pour cette application.", "app_info_default_desc": "Redirige la racine du domaine vers cette application (%s).", "app_info_uninstall_desc": "Supprimer cette application.", @@ -444,5 +444,20 @@ "request_help": "besoin d'assistance", "request_help_details": "Le responsable actuel aimerait de l'aide pour la maintenance de cette application. N'hésitez pas à y contribuer !", "advanced": "Avancée", - "from_to": "de %s à %s" + "from_to": "de %s à %s", + "group_name": "Nom du groupe", + "nobody": "Personne", + "group": "Groupe", + "group_all_users": "Tous les utilisateurs", + "group_visitors": "Visiteurs", + "group_format_name_help": "Vous pouvez utiliser des caractères alphanumériques et des espaces", + "group_add_member": "Ajouter un utilisateur", + "group_add_permission": "Ajouter une permission", + "group_new": "Nouveau groupe", + "group_explain_all_users": "Ceci est un groupe spécial contenant tous les comptes d'utilisateurs sur le serveur", + "group_explain_visitors": "Ceci est un groupe spécial représentant les visiteurs anonymes", + "group_specific_permissions": "Autorisations spécifiques à l'utilisateur", + "groups_and_permissions": "Groupes et autorisations", + "groups_and_permissions_manage": "Gérer les groupes et les autorisations", + "permissions": "Permissions" } diff --git a/src/locales/tr.json b/src/locales/tr.json index e50ea99e..696dcc83 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -3,7 +3,7 @@ "administration_password": "Yönetici parolası", "api_not_responding": "API cevap vermiyor", "both": "İkisi birden", - "cancel": "İptal et", + "cancel": "İptal etmek", "close": "Kapat", "confirm_access_add": "%s için bütünn kullanıcılara erişim vermek istediğinizden emin misiniz ?", "confirm_access_remove_all": "%s'e bütün erişimleri kaldırmak istediğinizden emin misiniz ?", @@ -70,5 +70,6 @@ "url": "URL", "warning_first_user": "Büyük ihtimalle önce kullanıcı oluşturmanız gerekiyor.", "wrong_password": "Yanlış parola", - "yes": "Evet" + "yes": "Evet", + "ok": "Tamam" } diff --git a/src/views/app/app_debug.ms b/src/views/app/app_debug.ms deleted file mode 100644 index d233f1be..00000000 --- a/src/views/app/app_debug.ms +++ /dev/null @@ -1,39 +0,0 @@ - - -
- -{{#if services}} -
-{{#services}} -
- -
-
- {{#logs}} -

{{file_name}}

-
{{file_content}}
- - {{/logs}} -
-
-
-{{/services}} -
- -{{else}} -
- - {{t 'app_debug_no_logs'}} -
-{{/if}} diff --git a/src/views/app/app_info.ms b/src/views/app/app_info.ms index 53525c05..7c972a0e 100644 --- a/src/views/app/app_info.ms +++ b/src/views/app/app_info.ms @@ -57,9 +57,9 @@

{{t 'app_info_default_desc' settings.domain}}

- +

@@ -79,16 +79,9 @@

{{t 'app_info_uninstall_desc'}}

- +
-
-
-

{{t 'app_info_debug_desc'}}

- - {{t 'app_debug_tab'}} - +
diff --git a/src/views/backup/backup.ms b/src/views/backup/backup.ms index 4da53a75..e4bd4b0c 100644 --- a/src/views/backup/backup.ms +++ b/src/views/backup/backup.ms @@ -3,12 +3,6 @@ {{t 'backup'}} -
- -
-
diff --git a/src/views/backup/backup_info.ms b/src/views/backup/backup_info.ms index ae42c67a..117558a7 100644 --- a/src/views/backup/backup_info.ms +++ b/src/views/backup/backup_info.ms @@ -83,41 +83,11 @@
-

{{t 'backup_archive_delete'}}

- +
- {{#if other_storages}} -
-
-

{{t 'backup_archive_copy'}}

-
-
- -
- -
-
-
-
- -
-
-
-
- {{/if}}
diff --git a/src/views/backup/storage_create.ms b/src/views/backup/storage_create.ms deleted file mode 100644 index bc07a003..00000000 --- a/src/views/backup/storage_create.ms +++ /dev/null @@ -1,52 +0,0 @@ - - - -
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
- -
-
diff --git a/src/views/diagnosis/diagnosis_show.ms b/src/views/diagnosis/diagnosis_show.ms new file mode 100644 index 00000000..820ac417 --- /dev/null +++ b/src/views/diagnosis/diagnosis_show.ms @@ -0,0 +1,57 @@ + + +
+ +
+ +
+ +
{{t 'diagnosis_experimental_disclaimer'}}
+ +{{#reports}} +
+
+

+ {{ description }} +

+ {{#if noIssues}}{{t 'everything_good'}}{{/if}} + {{#if errors}}{{t 'errors' errors }}{{/if}} + {{#if warnings}}{{t 'warnings' warnings }}{{/if}} + {{#if ignored}}{{t 'ignored' ignored }}{{/if}} + +
+
+
    +

    {{t 'last_ran' }} {{formatRelative time day="numeric" month="long" year="numeric" hour="numeric" minute="numeric" }}

    + {{#items}} +
  • + {{#if icon}} + + {{/if}} + {{summary}} + {{#if ignored}} + + {{else}} + {{#if issue}} + + {{/if}} + {{/if}} + {{#if details}} + +
    +
      + {{#details}}
    • {{.}}
    • {{/details}} +
    +
    + {{/if}} +
  • + {{/items}} +
+
+
+{{/reports}} diff --git a/src/views/domain/domain_cert.ms b/src/views/domain/domain_cert.ms index 663f17ea..3969dfa3 100644 --- a/src/views/domain/domain_cert.ms +++ b/src/views/domain/domain_cert.ms @@ -46,36 +46,36 @@

{{t 'domain_not_eligible_for_ACME'}}

{{/if}} - +
{{/if}} {{#if actions_enabled.manual_renew_letsencrpt}}

{{t 'manually_renew_letsencrypt_message'}}

- +

{{/if}} {{#if actions_enabled.regen_selfsigned}}

{{t 'regenerate_selfsigned_cert_message'}}

- +

{{/if}} {{#if actions_enabled.replace_with_selfsigned}}

{{t 'revert_to_selfsigned_cert_message'}}

- +
{{/if}} diff --git a/src/views/domain/domain_info.ms b/src/views/domain/domain_info.ms index 6eed7627..50c341d1 100644 --- a/src/views/domain/domain_info.ms +++ b/src/views/domain/domain_info.ms @@ -12,67 +12,46 @@ {{name}} -
- {{#if main}} -

- {{t 'domain_default_longdesc'}} -

- {{/if}} -

{{url}}

- -
- - -
-
-

- {{t 'operations'}} -

-
- {{#unless main}}

{{t 'domain_default_desc'}}

-
- - -
+ {{#if main}} +

+ {{t 'domain_default_longdesc'}} +

+ {{else}} + + {{/if}}
- {{/unless}}

{{t 'certificate_manage'}}

- {{#unless enable_cert_management}} -

- {{t 'certificate_old_letsencrypt_app_conflict'}} -

- {{/unless}} - - {{t 'ssl_certificate'}} + + {{t 'ssl_certificate'}}

{{t 'domain_delete_longdesc' name}}

- - {{t 'delete'}} - +
diff --git a/src/views/home.ms b/src/views/home.ms index 2206cb28..526237c2 100644 --- a/src/views/home.ms +++ b/src/views/home.ms @@ -23,6 +23,10 @@

{{t 'tools'}}

+ + +

{{t 'diagnosis'}}

+

{{t 'backup'}}

diff --git a/src/views/service/service_info.ms b/src/views/service/service_info.ms index 3b8c7e19..2e78941d 100644 --- a/src/views/service/service_info.ms +++ b/src/views/service/service_info.ms @@ -53,25 +53,25 @@
{{#is_loaded}} - + {{/is_loaded}} {{^is_loaded}} - + {{/is_loaded}} {{#is_running}} - + {{/is_running}} {{^is_running}} - + {{/is_running}} {{t 'log'}} diff --git a/src/views/tools/tools_diagnosis.ms b/src/views/tools/tools_diagnosis.ms deleted file mode 100644 index ab94de03..00000000 --- a/src/views/tools/tools_diagnosis.ms +++ /dev/null @@ -1,25 +0,0 @@ - - -
- -
-
-

{{t 'diagnosis'}}

-
-
-
{{ diagnosis }}
- {{#if private}} - {{t 'diagnosis_hide_private'}} - {{else}} - {{t 'diagnosis_view_private'}} - {{/if}} - -
-
diff --git a/src/views/tools/tools_firewall.ms b/src/views/tools/tools_firewall.ms index a22ef665..c03c03d3 100644 --- a/src/views/tools/tools_firewall.ms +++ b/src/views/tools/tools_firewall.ms @@ -29,20 +29,20 @@ {{#if this.ipv4}} - {{t 'close'}} + {{else}} - {{t 'open'}} + {{/if}} {{#if this.ipv6}} - {{t 'close'}} + {{else}} - {{t 'open'}} + {{/if}} @@ -75,20 +75,20 @@ {{#if this.ipv4}} - {{t 'close'}} + {{else}} - {{t 'open'}} + {{/if}} {{#if this.ipv6}} - {{t 'close'}} + {{else}} - {{t 'open'}} + {{/if}} @@ -168,10 +168,10 @@
{{#if upnp}}

{{t 'upnp_enabled'}}

- {{t 'disable'}} + {{else}}

{{t 'upnp_disabled'}}

- {{t 'enable'}} + {{/if}}
diff --git a/src/views/tools/tools_list.ms b/src/views/tools/tools_list.ms index 9aa13265..17943de7 100644 --- a/src/views/tools/tools_list.ms +++ b/src/views/tools/tools_list.ms @@ -6,10 +6,6 @@
- - -

{{t 'diagnosis'}}

-

{{t 'logs'}}

@@ -34,10 +30,6 @@

{{t 'advanced'}}

diff --git a/src/views/tools/tools_logs.ms b/src/views/tools/tools_logs.ms index b7f4d7b2..2b719534 100644 --- a/src/views/tools/tools_logs.ms +++ b/src/views/tools/tools_logs.ms @@ -22,7 +22,7 @@
diff --git a/src/views/tools/tools_migrations.ms b/src/views/tools/tools_migrations.ms index 8ffbcedf..3536aee7 100644 --- a/src/views/tools/tools_migrations.ms +++ b/src/views/tools/tools_migrations.ms @@ -11,7 +11,7 @@

{{t 'migrations_pending'}} {{#if pending_migrations}}
- {{t 'run'}} +
{{/if}}

@@ -24,7 +24,7 @@

{{ number }}. {{ description }}
- {{t 'skip'}} +

{{#if disclaimer }} diff --git a/src/views/tools/tools_monitoring.ms b/src/views/tools/tools_monitoring.ms deleted file mode 100644 index 1593be1b..00000000 --- a/src/views/tools/tools_monitoring.ms +++ /dev/null @@ -1,283 +0,0 @@ - - -
- -{{#if status}} -
-
-

{{t 'infos'}}

-
-
-
-
{{t 'hostname'}}
-
{{system.infos.hostname}}
-
{{t 'os'}}
-
{{ucwords system.infos.linux_distro}} {{system.infos.platform}} ({{system.infos.os_name}} {{system.infos.os_version}})
-
{{t 'uptime'}}
-
{{system.uptime}}
-
-
-
- -
-
-

{{t 'versions'}}

-
-
-
- {{#each versions}} -
{{@key}}
-
{{version}} ({{repo}})
- {{/each}} -
-
-
- -
- -
-
-

- - {{t 'check'}} -

-
-
-
-
-
{{t 'check_stmp'}}
-
{{network.check.smtp_check}}
-
{{t 'check_mx'}}
-
- {{#if network.check.mx_check.mx0}} -
    - {{#each network.check.mx_check}} -
  • {{this}}
  • - {{/each}} -
- {{else}} - {{network.check.mx_check}} - {{/if}} -
-
-
-
-
- -
-
-

- - {{t 'system'}} -

-
-
-
- -
-

{{t 'memory'}}

-

{{t 'ram'}}

- - - - - - - - - - - - - -
{{t 'used'}}{{humanSize system.memory.ram.used}} ({{system.memory.ram.percent}} %)
{{t 'free'}}{{humanSize system.memory.ram.free}}
{{t 'total'}}{{humanSize system.memory.ram.total}}
- -

{{t 'swap'}}

- - - - - - - - - - - - - -
{{t 'used'}}{{humanSize system.memory.swap.used}} ({{system.memory.swap.percent}} %)
{{t 'free'}}{{humanSize system.memory.swap.free}}
{{t 'total'}}{{humanSize system.memory.swap.total}}
-
- -
-

{{t 'cpu_load'}}

- - - - - - - - - - - - - -
{{t 'count_min' "1"}}{{system.cpu.load.min1}}
{{t 'count_min' "5"}}{{system.cpu.load.min5}}
{{t 'count_min' "15"}}{{system.cpu.load.min15}}
-
- -
-

{{t 'process'}}

- - - - - - - - - - - - - -
{{t 'running'}}{{system.process.running}}
{{t 'sleeping'}}{{system.process.sleeping}}
{{t 'total'}}{{system.process.total}}
-
-
-
-
- -
-
-

- - {{t 'network'}} -

-
-
-
- {{t 'public_ip'}}{{network.infos.public_ip}} -
- {{t 'gateway'}}{{network.infos.gateway}} - -

{{t 'local_ip'}}

- - - - - - - - - - {{#each network.infos.local_ip}} - - - - - - {{/each}} - -
{{t 'interface'}}{{t 'ipv4'}}{{t 'ipv6'}}
{{@key}}{{ ipv4 }}{{ ipv6 }}
- -

{{t 'usage'}}

- {{#each network.usage}} -
- - - - - - - - - - - - - - - - - - - - -
-

- {{@key}} - {{t 'time_since_update'}}{{humanTime time_since_update}} -

-
{{t 'bit_rate'}}{{t 'cumulative_usage'}}
{{t 'transmission'}}{{bitRate tx time_since_update}}{{humanSize cumulative_tx}}
{{t 'reception'}}{{bitRate rx time_since_update}}{{humanSize cumulative_rx}}
-
- {{/each}} -
-
-
- -
-
-

- - {{t 'disk'}} -

-
-
-
- {{#each disk}} -
-

{{@key}}

-
-
-

{{t 'filesystem'}}

- - - - - - - - - - - - - - - - -
{{t 'fs_type'}}{{ filesystem.fs_type }}
{{t 'mount_point'}}{{ filesystem.mnt_point }}
{{t 'size'}}{{humanSize filesystem.size }}
{{t 'used'}}{{humanSize filesystem.used }}
{{t 'available'}}{{humanSize filesystem.avail }}
-
-
-

{{t 'io'}} {{t 'time_since_update'}}{{humanTime io.time_since_update }}

- - - - - - - -
{{t 'read'}}{{humanSize io.read_bytes }}
{{t 'write'}}{{humanSize io.write_bytes }}
-
-
- {{/each}} -
-
-
- -
- -{{else}} -
- - {{t 'monitoring_disabled'}} -
- {{t 'monitoring_check_glances'}} -
- -{{/if}} diff --git a/src/views/tools/tools_reboot.ms b/src/views/tools/tools_reboot.ms index c66bc0a0..ef184eaf 100644 --- a/src/views/tools/tools_reboot.ms +++ b/src/views/tools/tools_reboot.ms @@ -15,14 +15,14 @@
diff --git a/src/views/tools/tools_rebooting.ms b/src/views/tools/tools_rebooting.ms deleted file mode 100644 index 1d4d95cc..00000000 --- a/src/views/tools/tools_rebooting.ms +++ /dev/null @@ -1 +0,0 @@ -
{{t 'tools_rebooting'}}
diff --git a/src/views/tools/tools_shuttingdown.ms b/src/views/tools/tools_shuttingdown.ms deleted file mode 100644 index e7294efd..00000000 --- a/src/views/tools/tools_shuttingdown.ms +++ /dev/null @@ -1 +0,0 @@ -
{{t 'tools_shuttingdown'}}
diff --git a/src/views/update/update.ms b/src/views/tools/tools_update.ms similarity index 75% rename from src/views/update/update.ms rename to src/views/tools/tools_update.ms index 62a44b50..ec999c3c 100644 --- a/src/views/update/update.ms +++ b/src/views/tools/tools_update.ms @@ -20,7 +20,7 @@ {{/system}}
{{else}}
@@ -37,14 +37,14 @@
{{#apps}}
- {{t 'system_upgrade_btn'}} +

{{label}} {{id}}

-

{{t 'from_to' current_version new_version}}

+ {{t 'from_to' current_version new_version}}
{{/apps}}
{{else}}
diff --git a/src/views/upgrade/upgrade.ms b/src/views/upgrade/upgrade.ms deleted file mode 100644 index cf8d087f..00000000 --- a/src/views/upgrade/upgrade.ms +++ /dev/null @@ -1,18 +0,0 @@ - - -
- -{{#if logs}} -
-{{#logs}}
-    {{.}}
-{{/logs}}
-
- -{{else}} - {{t 'no_log'}} -{{/if}} diff --git a/src/views/user/group_list.ms b/src/views/user/group_list.ms index 7b68e404..a6643f79 100644 --- a/src/views/user/group_list.ms +++ b/src/views/user/group_list.ms @@ -17,10 +17,10 @@ {{text}} - + {{/inline}} @@ -35,7 +35,7 @@
@@ -53,10 +53,10 @@ {{#if special}}{{t (concat 'group_' @key)}}{{else}}{{t 'group'}} "{{ucwords @key}}"{{/if}} {{#unless special}} - + {{/unless}}
@@ -122,7 +122,7 @@ {{#each groups}} {{#if primary}} {{#unless (or permissions display)}} -
  • {{@key}}
  • +
  • {{/unless}} {{/if}} {{/each}} diff --git a/src/views/user/user_info.ms b/src/views/user/user_info.ms index ccca3788..7262aa89 100644 --- a/src/views/user/user_info.ms +++ b/src/views/user/user_info.ms @@ -62,7 +62,7 @@ {{t 'user_username_edit' username}} - {{t 'delete'}} +