diff --git a/debian/changelog b/debian/changelog index 5051420c..8a0f828a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,63 @@ +yunohost-admin (2.7.5) stable; urgency=low + + (Bumping version number for stable release) + + -- Alexandre Aubin Sat, 02 Dec 2017 12:34:45 -0500 + +yunohost-admin (2.7.4) testing; urgency=low + + * [i18n] Improve French. German translations + + -- Alexandre Aubin Tue, 28 Nov 2017 18:58:04 -0500 + +yunohost-admin (2.7.3) testing; urgency=low + + * [i18n] Improve french translation (#170) + * [fix] Add "OK" in translatable strings (#171) + * [enh] Be able to upgrade single apps (#172) + * [enh] Reboot/shutdown from admin interface (#173) + +Thanks to all contributors <3 ! (opi, ljf, ariasuni, Jibec) + + -- Alexandre Aubin Thu, 12 Oct 2017 16:51:24 -0400 + +yunohost-admin (2.7.2) stable; urgency=low + +Releasing as stable + + -- Alexandre Aubin Tue, 22 Aug 2017 21:36:35 -0400 + +yunohost-admin (2.7.1) testing; urgency=low + + [ Alexandre Aubin ] + * [fix] Tell user that domain dns-conf shows a recommendation only + + [ Translations ] + * Added translation using Weblate (Russian) (Evgeniy Ozhiganov) + * [i18n] Translated using Weblate (Esperanto) (MCMic) + +Thanks to all contributors (MCMic, Aleks, Ozhiganov) <3 ! + + -- Laurent Peuch Sat, 19 Aug 2017 22:43:15 +0000 + +yunohost-admin (2.7.0) testing; urgency=low + + * [enh] Variable assignment code cleanup. (#161) + * [fix] Friendlier and more meaningful 'error 500' (#166) + * [i18n] Started Russian translation (#167) + +Thanks to all contributors (opi, Aleks, Ozhiganov) <3 ! + + -- Alexandre Aubin Mon, 07 Aug 2017 12:49:55 -0400 + +yunohost-admin (2.6.2) stable; urgency=low + +## Minor fix + + * 'hooks' key is now 'system' in backup info (#165) + + -- Alexandre Aubin Wed, 26 Jul 2017 12:09:03 -0400 + yunohost-admin (2.6.1) stable; urgency=low Major changes since 2.6.0 diff --git a/debian/control b/debian/control index 8d196903..5f02cf87 100644 --- a/debian/control +++ b/debian/control @@ -12,7 +12,7 @@ Architecture: all Conflicts: yunohost-apps-admin Replaces: yunohost-apps-admin Depends: ${misc:Depends} - , yunohost (>= 2.3.6) + , yunohost (>= 2.7.1) Description: web administration interface for yunohost YunoHost aims to make self-hosting accessible to everyone. It configures an email, Web and IM server alongside a LDAP base. It also provides diff --git a/src/js/yunohost/controllers/apps.js b/src/js/yunohost/controllers/apps.js index ee62d4aa..2dd11cf9 100644 --- a/src/js/yunohost/controllers/apps.js +++ b/src/js/yunohost/controllers/apps.js @@ -11,7 +11,7 @@ // List installed apps app.get('#/apps', function (c) { c.api('/apps?installed', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8 - apps = data['apps']; + var apps = data['apps']; c.arraySortById(apps); c.view('app/app_list', {apps: apps}); }); @@ -21,7 +21,7 @@ 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 - apps = []; + var apps = []; $.each(data['apps'], function(k, v) { // Keep only uninstalled apps, or multi-instance apps if ((!v['installed'] || dataraw[v['id']].manifest.multi_instance) && !v['id'].match(/__[0-9]{1,5}$/)) { @@ -169,7 +169,7 @@ // Helper function that build app installation form app.helper('appInstallForm', function(appId, manifest, params) { - data = { + var data = { id: appId, manifest: manifest }; @@ -286,10 +286,11 @@ // Clone a hidden input with empty value // https://stackoverflow.com/questions/476426/submit-an-html-form-with-empty-checkboxes - inputClone = {}; - inputClone.name = data.manifest.arguments.install[k].name; - inputClone.inputType = 'hidden'; - inputClone.default = 0; + var inputClone = { + name : data.manifest.arguments.install[k].name, + inputType : 'hidden', + default : 0 + }; data.manifest.arguments.install.push(inputClone); } @@ -319,13 +320,11 @@ // 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.appInstallForm( c.params['app'], data[c.params['app']].manifest, c.params ); - }); }); @@ -333,11 +332,14 @@ app.post('#/apps', function(c) { // Warn admin if app is going to be installed on domain root. if (c.params['path'] !== '/' || confirm(y18n.t('confirm_install_domain_root', [c.params['domain']]))) { - params = { 'label': c.params['label'], 'app': c.params['app'] }; - delete c.params['label']; - delete c.params['app']; + var params = { + label: c.params['label'], + app: c.params['app'] + }; // Check for duplicate arg produced by empty checkbox. (See inputClone) + delete c.params['label']; + delete c.params['app']; $.each(c.params, function(k, v) { if (typeof(v) === 'object' && Array.isArray(v)) { // And return only first value @@ -346,6 +348,7 @@ }); params['args'] = c.serialize(c.params.toHash()); + // Do not pass empty args. if (params['args'] === "") { delete params['args']; @@ -365,7 +368,10 @@ // Install custom app from github app.post('#/apps/install/custom', function(c) { - params = { 'label': c.params['label'], 'app': c.params['url'] }; + var params = { + label: c.params['label'], + app: c.params['url'] + }; delete c.params['label']; delete c.params['url']; @@ -475,7 +481,10 @@ y18n.t('applications'), y18n.t('confirm_access_remove_all', [c.params['app']]), function() { - params = {'apps': c.params['app'], 'users':[]}; + var params = { + apps: c.params['app'], + users: [] + }; c.api('/access?'+c.serialize(params), function(data) { // http://api.yunohost.org/#!/app/app_removeaccess_delete_12 store.clear('slide'); c.redirect('#/apps/'+ c.params['app']+ '/access'); @@ -494,7 +503,10 @@ y18n.t('applications'), y18n.t('confirm_access_remove_user', [c.params['app'], c.params['user']]), function() { - params = {'apps': c.params['app'], 'users': c.params['user']}; + var params = { + apps: c.params['app'], + users: c.params['user'] + }; c.api('/access?'+c.serialize(params), function(data) { // http://api.yunohost.org/#!/app/app_removeaccess_delete_12 store.clear('slide'); c.redirect('#/apps/'+ c.params['app']+ '/access'); @@ -513,7 +525,10 @@ y18n.t('applications'), y18n.t('confirm_access_add', [c.params['app']]), function() { - params = {'apps': c.params['app'], 'users': null}; + var params = { + apps: c.params['app'], + users: null + }; c.api('/access', function() { // http://api.yunohost.org/#!/app/app_addaccess_put_13 store.clear('slide'); c.redirect('#/apps/'+ c.params['app'] +'/access'); @@ -528,7 +543,10 @@ // Grant access for a specific user app.post('#/apps/:app/access/add', function (c) { - params = {'users': c.params['user'], 'apps': c.params['app']}; + var params = { + users: c.params['user'], + apps: c.params['app'] + }; c.api('/access', function() { // http://api.yunohost.org/#!/app/app_addaccess_put_13 store.clear('slide'); c.redirect('#/apps/'+ c.params['app'] +'/access'); @@ -541,7 +559,9 @@ y18n.t('applications'), y18n.t('confirm_access_clear', [c.params['app']]), function() { - params = {'apps': c.params['app']}; + var params = { + apps: c.params['app'] + }; c.api('/access', function() { // store.clear('slide'); c.redirect('#/apps/'+ c.params['app'] +'/access'); @@ -592,4 +612,50 @@ }, 'PUT', params); }); + // 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 + + // Display a list of available domains + var domains = []; + $.each(domain_data.domains, function(k, domain) { + domains.push({ + value: domain, + label: domain, + // Select current domain + selected: (domain == app_data.settings.domain ? true : false) + }); + }); + + data = { + id: c.params['app'], + label: app_data.manifest.name, + domains: domains, + // Pre-fill with current path + path: app_data.settings.path + }; + c.view('app/app_changeurl', data); + }); + }); + }); + + // Change app URL + app.post('#/apps/:app/changeurl', function (c) { + c.confirm( + y18n.t('applications'), + y18n.t('confirm_app_change_url', [c.params['app']]), + function() { + params = {'domain': c.params['domain'], 'path': c.params['path']}; + c.api('/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'); + } + ); + }); })(); diff --git a/src/js/yunohost/controllers/backup.js b/src/js/yunohost/controllers/backup.js index dad40d4d..9511b6fc 100644 --- a/src/js/yunohost/controllers/backup.js +++ b/src/js/yunohost/controllers/backup.js @@ -18,14 +18,15 @@ 'system_yunohost', 'system_nginx' ]; + // Storage list app.get('#/backup', function (c) { var storages = []; var item = { - id: 'local', - name: y18n.t('local_archives'), - uri: '/home/yunohost.backup/' - }; + id: 'local', + name: y18n.t('local_archives'), + uri: '/home/yunohost.backup/' + }; storages.push(item); c.view('backup/backup', {'storages':storages}); @@ -44,14 +45,14 @@ // Create a backup app.get('#/backup/:storage/create', function (c) { - var data=[]; - data['storage']={ + 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']={}; + 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); @@ -74,13 +75,13 @@ y18n.t('backup'), y18n.t('confirm_restore', [c.params['archive']]), $.proxy(function(c){ - var params=c.ungroupHooks(c.params['hooks'],c.params['apps']); - params['force']=''; + var params = c.ungroupHooks(c.params['hooks'],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), + }, this, c), function(){ store.clear('slide'); c.redirect('#/backup/'+ c.params['storage']+'/'+c.params['archive']); @@ -127,14 +128,14 @@ // 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.storage = { + id: c.params['storage'], + name: y18n.t('local_archives') }; - data['other_storages']=[]; - data['name']=c.params['archive']; - data['hooks']=c.groupHooks(Object.keys(data['system'])); - data['items']=(data['hooks']!={} || data['apps']!=[]); + data.other_storages = []; + data.name = c.params['archive']; + data.hooks = c.groupHooks(Object.keys(data['system'])); + data.items = (data['hooks']!={} || data['apps']!=[]); c.view('backup/backup_info', data); }); }); @@ -142,16 +143,16 @@ // Archive list app.get('#/backup/:storage', function (c) { c.api('/backup/archives?with_info', function(data) { - data['storage']={ - id:'local', - name:y18n.t('local_archives') + data.storage = { + id: 'local', + name: y18n.t('local_archives') }; - data['archives2']=[]; + data.archives2 = []; $.each(data['archives'], function(name, info) { - info['name']=name; - data['archives2'].unshift(info) + info.name = name; + data.archives2.unshift(info) }); - data['archives']=data['archives2']; + data.archives = data.archives2; c.view('backup/backup_list', data); }); }); diff --git a/src/js/yunohost/controllers/domains.js b/src/js/yunohost/controllers/domains.js index e201ef00..94096d4b 100644 --- a/src/js/yunohost/controllers/domains.js +++ b/src/js/yunohost/controllers/domains.js @@ -12,7 +12,7 @@ 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) { - domains = []; + var domains = []; $.each(data.domains, function(k, domain) { domains.push({ url: domain, @@ -21,11 +21,14 @@ }); // Do not show main domain form if we have only 1 domain - main_domain_form = (domains.length > 1) ? true: false; + var main_domain_form = (domains.length > 1) ? true: false; // Sort domains with main domain first domains.sort(function(a, b){ return -2*(a.main) + 1; }); - c.view('domain/domain_list', {domains: domains, main_domain_form: main_domain_form}); + c.view('domain/domain_list', { + domains: domains, + main_domain_form: main_domain_form + }); }, 'PUT'); }); }); @@ -40,10 +43,10 @@ c.params.ddomains = ['.nohost.me', '.noho.st']; }) .always(function() { - data = { - ddomains : c.params.ddomains, - domains : c.params.domains, - allowDyndnsDomain : true + var data = { + ddomains: c.params.ddomains, + domains: c.params.domains, + allowDyndnsDomain: true }; // Allow only 1 DynDns domain. @@ -60,17 +63,18 @@ // Add domain (POST) app.post('#/domains/add', function (c) { + var params = {}; + var endurl = ''; if (c.params['domain'] === '') { if (c.params['ddomain'] === '') { c.flash('fail', y18n.t('error_select_domain')); store.clear('slide'); c.redirect('#/domains/add'); } - params = {'domain': c.params['ddomain'] + c.params['ddomain-ext']}; + params.domain = c.params['ddomain'] + c.params['ddomain-ext']; endurl = 'dyndns'; } else { - params = { 'domain': c.params['domain'] }; - endurl = ''; + params.domain = c.params['domain']; } c.api('/domains?'+endurl, function(data) { // http://api.yunohost.org/#!/domain/domain_add_post_1 @@ -87,16 +91,15 @@ // for apps installed) should be removed once letsencrypt_ynh // is not used by many people anymore. Probably around 07/2017 // or end of 2017... - enable_cert_management_ = true; + var enable_cert_management_ = true; $.each(data['apps'], function(k, v) { - if (v.id == "letsencrypt") - { + if (v.id == "letsencrypt") { enable_cert_management_ = false; } }); - domain = { + var domain = { name: c.params['domain'], main: (c.params['domain'] == dataMain.current_main_domain) ? true : false, url: "https://"+c.params['domain'], @@ -110,10 +113,10 @@ // Domain DNS app.get('#/domains/:domain/dns', function (c) { c.api('/domains/' + c.params['domain'] + '/dns', function(data) { - domain = { + var domain = { name: c.params['domain'], dns: data - } + }; c.view('domain/domain_dns', domain); }); }); @@ -122,16 +125,15 @@ app.get('#/domains/:domain/cert-management', function (c) { c.api('/domains/cert-status/' + c.params['domain'] + '?full', function(data) { - s = data["certificates"][c.params['domain']] - - status_ = {} - status_.CA_type = s.CA_type.verbose - status_.CA_name = s.CA_name - status_.validity = s.validity - status_.ACME_eligible = s.ACME_eligible + var s = data["certificates"][c.params['domain']]; + var status_ = { + CA_type: s.CA_type.verbose, + CA_name: s.CA_name, + validity: s.validity, + ACME_eligible: s.ACME_eligible + }; - switch (s.summary.code) - { + switch (s.summary.code) { case "critical" : status_.alert_type = "danger"; status_.alert_icon = "exclamation-circle" ; @@ -143,14 +145,12 @@ status_.alert_message = y18n.t('certificate_alert_selfsigned'); break; case "attention" : - if (status_.CA_type == "lets-encrypt") - { + if (status_.CA_type == "lets-encrypt") { status_.alert_type = "warning"; status_.alert_icon = "clock-o"; status_.alert_message = y18n.t('certificate_alert_letsencrypt_about_to_expire'); } - else - { + else { status_.alert_type = "danger"; status_.alert_icon = "clock-o"; status_.alert_message = y18n.t('certificate_alert_about_to_expire'); @@ -173,14 +173,14 @@ break; } - actions_enabled = {}; - actions_enabled.install_letsencrypt = false; - actions_enabled.manual_renew_letsencrpt = false; - actions_enabled.regen_selfsigned = false; - actions_enabled.replace_with_selfsigned = false; + var actions_enabled = { + install_letsencrypt: false, + manual_renew_letsencrpt: false, + regen_selfsigned: false, + replace_with_selfsigned: false + }; - switch (s.CA_type.code) - { + switch (s.CA_type.code) { case "self-signed" : actions_enabled.install_letsencrypt = true; actions_enabled.regen_selfsigned = true; @@ -305,14 +305,16 @@ y18n.t('domains'), y18n.t('confirm_change_maindomain'), function(){ - params = {'new_domain': c.params['domain']}; + 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 - refreshDomain = window.setTimeout(function(){ + var refreshDomain = window.setTimeout(function(){ store.clear('slide'); c.redirect('#/domains'); }, 15000); diff --git a/src/js/yunohost/controllers/firewall.js b/src/js/yunohost/controllers/firewall.js index 01b90159..beeff865 100644 --- a/src/js/yunohost/controllers/firewall.js +++ b/src/js/yunohost/controllers/firewall.js @@ -12,8 +12,8 @@ app.get('#/tools/firewall', function (c) { c.api('/firewall?raw', function(data) { var firewall = { - ports : {}, - upnp : false + ports: {}, + upnp: false }; // Reorganize ports @@ -41,7 +41,9 @@ // confirm_upnp_enable and confirm_upnp_disable y18n.t('confirm_upnp_' + c.params['action'].toLowerCase()), function(){ - params = {'action' : c.params['action']}; + var params = { + action : c.params['action'] + }; c.api('/firewall/upnp', function(data) { store.clear('slide'); c.redirect('#/tools/firewall'); @@ -57,8 +59,8 @@ // Toggle port status helper (available in every controller) app.helper('togglePort', function(port, protocol, connection, action) { var method = null, - endurl = [], - c = this + endurl = [], + c = this ; if (port != parseInt(port) || port < 0 || port > 65535) { @@ -110,8 +112,8 @@ // --ipv6-only: // --no-upnp: var params = { - 'port' : port, - 'protocol' : protocol, + port : port, + protocol : protocol }; c.api('/firewall/port?'+endurl, function(data) { store.clear('slide'); diff --git a/src/js/yunohost/controllers/home.js b/src/js/yunohost/controllers/home.js index 8f9c78c0..8f06867c 100644 --- a/src/js/yunohost/controllers/home.js +++ b/src/js/yunohost/controllers/home.js @@ -35,9 +35,10 @@ // Loop through items in a reverse order (older first) $($('item', xml).get().reverse()).each(function(k, v) { - var link=$('link', v).text(); - if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') - link=forumUrl+link; + var link = $('link', v).text(); + if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') { + link = forumUrl+link; + } // var description=$('description', v).text(); // description=description.replace('href="/','href="'+forumUrl+'/'); @@ -70,6 +71,13 @@ c.flash('fail', y18n.t('error_retrieve_feed', [securityFeed])); }); + c.api("/diagnosis", function(data) { + console.log(data); + if (data.security["CVE-2017-5754"].vulnerable) { + c.flash('danger', y18n.t('meltdown')); + } + }); + c.view('home'); }); }); @@ -138,8 +146,8 @@ // Store url from params, it could have change form 'run' state store.set('url', c.params['domain'] +'/yunohost/api'); - params = { - 'password': c.params['password'] + var params = { + password: c.params['password'] }; c.api('/login', function(data) { store.set('connected', true); diff --git a/src/js/yunohost/controllers/monitor.js b/src/js/yunohost/controllers/monitor.js index 3dbbd72d..a7e8c664 100644 --- a/src/js/yunohost/controllers/monitor.js +++ b/src/js/yunohost/controllers/monitor.js @@ -10,7 +10,7 @@ // Server monitoring app.get('#/tools/monitor', function (c) { - monitorData = {}; + var monitorData = {}; // Why this method ? c.api('/services/glances', function(data) { // ? diff --git a/src/js/yunohost/controllers/postinstall.js b/src/js/yunohost/controllers/postinstall.js index e775b778..8f9834be 100644 --- a/src/js/yunohost/controllers/postinstall.js +++ b/src/js/yunohost/controllers/postinstall.js @@ -25,10 +25,10 @@ $('#masthead').hide(); $.get('https://dyndns.yunohost.org/domains', function() {}) .done(function(data){ - c.params.ddomains = data.map(function(dom){return '.'+dom;}); + c.params['ddomains'] = data.map(function(dom){return '.'+dom;}); }) .fail(function() { - c.params.ddomains = ['.nohost.me', '.noho.st']; + c.params['ddomains'] = ['.nohost.me', '.noho.st']; }) .always(function() { c.view('postinstall/postinstall_2', c.params, function() { @@ -77,14 +77,16 @@ store.clear('slide'); c.redirect('#/postinstall/domain'); } else { - params = { 'domain': c.params['domain'].toLowerCase() }; + 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']; + params.password = c.params['password']; store.set('url', window.location.hostname +'/yunohost/api'); store.set('user', 'admin'); diff --git a/src/js/yunohost/controllers/services.js b/src/js/yunohost/controllers/services.js index 2a03c744..49a59dd4 100644 --- a/src/js/yunohost/controllers/services.js +++ b/src/js/yunohost/controllers/services.js @@ -11,7 +11,9 @@ // All services status app.get('#/services', function (c) { c.api('/services', function(data) { // ? - data2 = { 'services': [] }; + var data2 = { + services: [] + }; $.each(data, function(k, v) { v.name = k; // Handlebars want booleans @@ -29,7 +31,9 @@ // Status & actions for a service app.get('#/services/:service', function (c) { c.api('/services/'+ c.params['service'], function(data) { // ? - data2 = { 'service': data }; + var data2 = { + service: data + }; data2.service.name = c.params['service']; // Handlebars want booleans data2.service.is_loaded = (data.loaded=='enabled') ? true : false; @@ -44,7 +48,9 @@ // Service log app.get('#/services/:service/log', function (c) { - params = { 'number': 50 }; + var params = { + number: 50 + }; c.api('/services/'+ c.params['service'] +'/log', function(data) { // ? data2 = { 'logs': [], 'name': c.params['service'] }; $.each(data, function(k, v) { @@ -62,7 +68,8 @@ // 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']; + var method = null, + endurl = c.params['service']; switch (c.params['action']) { case 'start': diff --git a/src/js/yunohost/controllers/tools.js b/src/js/yunohost/controllers/tools.js index 81d6f6d4..752d3740 100644 --- a/src/js/yunohost/controllers/tools.js +++ b/src/js/yunohost/controllers/tools.js @@ -20,7 +20,7 @@ // Update administration password (PUT) app.put('#/tools/adminpw', function (c) { - params = {}; + var params = {}; $.each(c.params.toHash(), function(key, value) { if (value !== '') { params[key] = value; } }); @@ -49,7 +49,7 @@ // System update & upgrade app.get('#/update', function (c) { c.api('/update', function(data) { - packagesLength = data.packages.length; + var packagesLength = data.packages.length; for(var i = 0; i < packagesLength; i++) { data.packages[i].delayed = false; data.packages[i].changelog = data.packages[i].changelog.replace(/\n/g, '
'); @@ -77,7 +77,7 @@ // confirm_update_apps and confirm_update_packages y18n.t('confirm_update_' + c.params['type'].toLowerCase()), function(){ - endurl = ''; + var endurl = ''; if (c.params['type'] == 'packages') {endurl = 'ignore_apps';} else if (c.params['type'] == 'apps') {endurl = 'ignore_packages';} @@ -95,6 +95,27 @@ ); } }); + + // 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) { + // 'log' is a reserved name, maybe in handlebars + data.logs = data.log; + c.view('upgrade/upgrade', data); + }, 'PUT'); + }, + function(){ + store.clear('slide'); + c.redirect('#/update'); + } + ); + }); + // Download SSL Certificate Authority app.get('#/tools/ca', function (c) { @@ -103,7 +124,7 @@ // Security feed app.get('#/tools/security-feed', function (c) { - data = { + var data = { items: [] }; @@ -125,11 +146,12 @@ .done(function(xml){ // Loop through items $('item', xml).each(function(k, v) { - var link=$('link', v)[0].innerHTML; - if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') - link=forumUrl+link; - var description=$('description', v)[0].textContent; - description=description.replace('href="/','href="'+forumUrl+'/'); + var link = $('link', v)[0].innerHTML; + if (typeof link == 'string' && link !== '' && link.charAt(0) == '/') { + link = forumUrl+link; + } + var description = $('description', v)[0].textContent; + description = description.replace('href="/','href="'+forumUrl+'/'); var item = { guid: $('guid', v)[0].innerHTML, @@ -156,12 +178,69 @@ }); }); + // Reboot or shutdown button + app.get('#/tools/reboot', function (c) { + c.view('tools/tools_reboot'); + }); + + // 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', '#/'); + + // 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(); + + // 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 - private = (c.params.splat[0] == 'private'); + var private = (c.params.splat[0] == 'private'); - endurl = (private) ? '?private' : ''; + var endurl = (private) ? '?private' : ''; c.api('/diagnosis'+endurl, function(diagnosis) { c.view('tools/tools_diagnosis', { 'diagnosis' : JSON.stringify(diagnosis, undefined, 4), @@ -171,4 +250,4 @@ }); }); -})(); \ No newline at end of file +})(); diff --git a/src/js/yunohost/controllers/users.js b/src/js/yunohost/controllers/users.js index 8b029eba..ebfa61fe 100644 --- a/src/js/yunohost/controllers/users.js +++ b/src/js/yunohost/controllers/users.js @@ -79,7 +79,7 @@ data.password_min_length = PASSWORD_MIN_LENGTH; // User email use a fake splitted field - email = data.mail.split('@'); + var email = data.mail.split('@'); data.email = { username : email[0], domain : email[1] @@ -156,7 +156,7 @@ c.params['mailalias'] = c.params['mailforward'] = ''; // Remove empty inputs - params = {}; + var params = {}; $.each(c.params.toHash(), function(key, value) { if (value.length > 0 && key !== 'user') { params[key] = value; } }); diff --git a/src/js/yunohost/events.js b/src/js/yunohost/events.js index 7a845d22..cde91e94 100644 --- a/src/js/yunohost/events.js +++ b/src/js/yunohost/events.js @@ -9,7 +9,7 @@ */ app.bind('login', function(e, data) { this.api('/version', function(versions) { - $('#yunohost-version').html(y18n.t('footer_version', [versions.yunohost])); + $('#yunohost-version').html(y18n.t('footer_version', [versions.yunohost.version, versions.yunohost.repo])); }); }); diff --git a/src/js/yunohost/helpers.js b/src/js/yunohost/helpers.js index 1c9e2a6a..4422abf5 100644 --- a/src/js/yunohost/helpers.js +++ b/src/js/yunohost/helpers.js @@ -62,10 +62,10 @@ }, // API call - api: function(uri, callback, method, data, websocket) { + api: function(uri, callback, method, data, websocket, callbackOnFailure) { c = this; - call = function(uri, callback, method, data) { + call = function(uri, callback, method, data, callbackOnFailure) { method = typeof method !== 'undefined' ? method : 'GET'; data = typeof data !== 'undefined' ? data : {}; if (window.navigator && window.navigator.language && (typeof data.locale === 'undefined')) { @@ -80,10 +80,75 @@ }, 1500); } - loaded = false; + app.loaded = false; if ($('div.loader').length === 0) { $('#main').append('
'); } + 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') { + if (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); + } else { + c.flash('fail', y18n.t('error_occured')); + } + } + // Regular errors + 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) { + error_log = JSON.parse(xhr.responseText); + error_log.route = error_log.route.join(' ') + '\n'; + error_log.arguments = JSON.stringify(error_log.arguments); + 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); + } + // Return HTTP error code at least + else { + var errorMessage = xhr.status+' '+xhr.statusText; + c.flash('fail', y18n.t('error_server_unexpected', [errorMessage])); + } + + // Remove loader if any + $('div.loader').remove(); + + // Force scrollTop on page load + $('html, body').scrollTop(0); + store.clear('slide'); + } + }; + } jQuery.ajax({ url: 'https://' + store.get('url') + uri, @@ -99,69 +164,14 @@ data = data || {}; callback(data); }) - .fail(function(xhr) { - // Postinstall is a custom case, we have to wait that - // operation is done before doing anything - if (uri === '/postinstall') { - if (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); - } else { - c.flash('fail', y18n.t('error_occured')); - } - } - // Regular errors - 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'); - } - } - // 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); - } - // Return HTTP error code at least - else { - var errorMessage = xhr.status+' '+xhr.statusText; - c.flash('fail', y18n.t('error_server_unexpected', [errorMessage])); - } - - // Remove loader if any - $('div.loader').remove(); - - // Force scrollTop on page load - $('html, body').scrollTop(0); - store.clear('slide'); - } - }); + .fail(callbackOnFailure); }; websocket = typeof websocket !== 'undefined' ? websocket : true; if (websocket) { // Open a WebSocket connection to retrieve live messages from the moulinette - ws = new WebSocket('wss://'+ store.get('url') +'/messages'); + var ws = new WebSocket('wss://'+ store.get('url') +'/messages'); ws.onmessage = function(evt) { // console.log(evt.data); $.each($.parseJSON(evt.data), function(k, v) { @@ -174,9 +184,9 @@ ws.onclose = function() {}; - ws.onopen = call(uri, callback, method, data); + ws.onopen = call(uri, callback, method, data, callbackOnFailure); } else { - call(uri, callback, method, data); + call(uri, callback, method, data, callbackOnFailure); } }, @@ -189,14 +199,14 @@ callback = typeof callback !== 'undefined' ? callback : function() {}; enableSlide = (typeof enableSlide !== 'undefined') ? enableSlide : true; // Change to false to disable animation - loaded = true; + app.loaded = true; // Hide loader and modal $('div.loader').remove(); $('#modal').modal('hide'); // Render content - rendered = this.render('views/'+ view +'.ms', data); + var rendered = this.render('views/'+ view +'.ms', data); // Update content helper var leSwap = function() { @@ -254,7 +264,7 @@ cancelCallback = typeof cancelCallback !== 'undefined' ? cancelCallback : function() {}; // Get modal element - box = $('#modal'); + var box = $('#modal'); // Modal title if (typeof title === 'string' && title.length) { @@ -312,8 +322,8 @@ }, groupHooks: function(hooks) { - data={}; - var rules=[ + var data = {}; + var rules = [ { id:'configuration', isIn:function (hook) { @@ -350,7 +360,7 @@ }, ungroupHooks: function(hooks,apps) { - var data={}; + var data = {}; data['apps'] = apps || []; data['hooks'] = hooks || []; diff --git a/src/js/yunohost/main.js b/src/js/yunohost/main.js index 595c218b..fd566a82 100644 --- a/src/js/yunohost/main.js +++ b/src/js/yunohost/main.js @@ -42,6 +42,23 @@ return new Handlebars.SafeString(result); }); + // Block helper to add a tooltip to any element + Handlebars.registerHelper('tooltip', function(tooltip, options) { + return new Handlebars.SafeString( + '' + + options.fn(this) + + ''); + }); + + // Load tooltips on the page; needed if using tooltips + Handlebars.registerHelper('load_tooltips', function() { + return new Handlebars.SafeString( + ''); + }); // Look for supported type of storage to use /** @@ -77,7 +94,7 @@ // Get YunoHost version if (sam.store.get('connected')) { this.api('/version', function(versions) { - $('#yunohost-version').html(y18n.t('footer_version', [versions.yunohost])); + $('#yunohost-version').html(y18n.t('footer_version', [versions.yunohost.version, versions.yunohost.repo])); }); } @@ -133,4 +150,4 @@ app.run('#/'); }); -})(); \ No newline at end of file +})(); diff --git a/src/js/yunohost/y18n.js b/src/js/yunohost/y18n.js index 3d8c6848..daa128bd 100644 --- a/src/js/yunohost/y18n.js +++ b/src/js/yunohost/y18n.js @@ -6,7 +6,7 @@ defaultLocale: "en", locale: "en", placeholder: /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm, - translations: {}, + translations: {} }; /** diff --git a/src/locales/de.json b/src/locales/de.json index a732b042..10300cdb 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -335,5 +335,34 @@ "confirm_upnp_enable": "Möchtest du wirklich UPnP aktivieren?", "confirm_upnp_disable": "Möchtest du wirklich UPnP deaktivieren?", "confirm_firewall_open": "Möchtest du wirklich Port %s1 öffnen? (Protokoll: %s2, Verbindung: %s3)", - "confirm_firewall_close": "Möchtest du wirklich Port %s1 schließen? (Protokoll: %s2, Verbindung: %s3)" + "confirm_firewall_close": "Möchtest du wirklich Port %s1 schließen? (Protokoll: %s2, Verbindung: %s3)", + "remove": "Entfernen", + "confirm_update_specific_app": "Möchtest du wirklich %s aktualisieren?", + "confirm_reboot_action_reboot": "Möchtest du wirklich den Server neustarten?", + "confirm_reboot_action_shutdown": "Möchtest du wirklich den Server herunterfahren?", + "domain_dns_conf_is_just_a_recommendation": "Diese Seite zeigt dir die *empfohlene* Konfiguration. Sie konfiguriert *nicht* den DNS für dich. Es liegt in deiner Verantwortung, deine Zone bei deinem Registrar entsprechend dieser Empfehlung zu konfigurieren.", + "ok": "OK", + "system_upgrade_all_applications_btn": "Aktualisiere alle Applikationen", + "system_upgrade_all_packages_btn": "Aktualisiere alle Pakete", + "tools_reboot": "Starte deine Server neu", + "tools_reboot_btn": "Neustart", + "tools_reboot_done": "Starte neu...", + "tools_rebooting": "Dein Server startet neu. Um zur Verwaltungsoberfläche zurückzukehren, musst du warten bis der Server hochgefahren ist. Feststellen kannst du es, in dem du die Seite neu lädst.", + "tools_shutdown": "Fahre deinen Server herunter", + "tools_shutdown_btn": "Herunterfahren", + "tools_shutdown_done": "Fahre herunter...", + "tools_shuttingdown": "Dein Server wird heruntergefahren. Solange dein Server ausgeschaltet ist, kannst du das Verwaltungsoberfläche nicht benutzen.", + "tools_shutdown_reboot": "Herunterfahren/Neustarten", + "appslists": "Applikationslisten", + "appslists_no_lists": "Keine Applikationslisten", + "appslists_custom": "Eigene Applikationslisten", + "appslists_manage": "Verwalte Applikationslisten", + "appslists_confirm_remove": "Möchtest du wirklich diese Applikationsliste entfernen?", + "appslists_info_refresh_desc": "Aktualisiere die Paketinformationen für diese Liste.", + "appslists_info_remove_desc": "Applikationen aus dieser Liste werden nicht mehr verfügbar sein.", + "appslists_last_update": "Letzte Aktualisierung", + "appslists_unknown_list": "Die Liste %s ist unbekannt", + "appslists_community_list": "Applikationsliste der Community", + "name": "Name", + "install_community_appslists_warning": "Nimm zur Kenntnis, dass diese Applikationen nicht offiziell sind und nicht von YunoHost gepflegt werden.
Diese Applikationen sind auf eigenes Risiko zu installieren und können dein System demolieren." } diff --git a/src/locales/en.json b/src/locales/en.json index 5a8aaeff..f2992c44 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -14,12 +14,15 @@ "app_access_removeall_desc": "No users will have access to %s.", "app_access_title": "%s access", "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": "Manage user access. Allowed users: %s", "app_info_changelabel_desc": "Change app label in the portal.", "app_info_debug_desc": "Display debugging information for this application.", "app_info_default_desc": "Redirect domain root to this application (%s).", + "app_info_changeurl_desc": "Change the access URL of this application (domain and/or path).", + "app_info_change_url_disabled_tooltip": "This feature hasn't been implemented in this app yet", "app_info_uninstall_desc": "Remove this application.", "app_install_cancel": "Installation cancelled.", "app_install_custom_no_manifest": "No manifest.json file", @@ -62,6 +65,7 @@ "confirm_access_clear": "Are you sure you want to clear all access to %s ?", "confirm_access_remove_all": "Are you sure you want to remove all access to %s ?", "confirm_access_remove_user": "Are you sure you want to remove access to %s for %s ?", + "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 ?", "confirm_change_maindomain": "Are you sure you want to change the main domain ?", "confirm_delete": "Are you sure you want to delete %s ?", @@ -78,8 +82,11 @@ "confirm_uninstall": "Are you sure you want to uninstall %s ?", "confirm_update_apps": "Are you sure you want to update all applications?", "confirm_update_packages": "Are you sure you want to update all packages?", + "confirm_update_specific_app": "Are you sure you want to update %s?", "confirm_upnp_enable": "Are you sure you want to enable UPnP?", "confirm_upnp_disable": "Are you sure you want to disable UPnP?", + "confirm_reboot_action_reboot": "Are you sure you want to reboot your server?", + "confirm_reboot_action_shutdown": "Are you sure you want to shutdown your server?", "connection": "Connection", "copy": "Copy", "count_min": "%s min", @@ -91,6 +98,7 @@ "default": "Default", "delete": "Delete", "description": "Description", + "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", @@ -129,7 +137,7 @@ "everyone_has_access": "Everyone has access.", "filesystem": "Filesystem", "firewall": "Firewall", - "footer_version" : "Powered by YunoHost %s.", + "footer_version" : "Powered by YunoHost %s (%s).", "form_input_example" : "Example: %s", "free": "Free", "fs_type": "FS Type", @@ -161,6 +169,7 @@ "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", @@ -197,6 +206,7 @@ "no_log": "No log.", "no_user_to_add": "No more users to add.", "non_compatible_api": "Non-compatible API", + "ok": "OK", "open": "Open", "operations": "Operations", "os": "OS", @@ -208,6 +218,7 @@ "passwords_dont_match": "Passwords don't match", "passwords_too_short": "Password is too short", "path": "Path", + "path_url": "Path", "port": "Port", "ports": "Ports", "postinstall": "Post-installation", @@ -254,6 +265,8 @@ "system_update": "System update", "system_upgrade": "System upgrade", "system_upgrade_btn": "Upgrade", + "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", @@ -268,6 +281,15 @@ "tools_security_feed_no_items": "No security notifications", "tools_security_feed_subscribe_rss": "Subscribe to security notifications RSS", "tools_security_feed_view_items": "View all security notifications", + "tools_reboot": "Reboot your server", + "tools_reboot_btn": "Reboot", + "tools_reboot_done": "Rebooting...", + "tools_rebooting": "Your server is rebooting. To return on the web administration interface you need to wait your server to be up. You can check that by refreshing regularly this page (F5).", + "tools_shutdown": "Shutdown your server", + "tools_shutdown_btn": "Shutdown", + "tools_shutdown_done": "Shutting down...", + "tools_shuttingdown": "Your server is powerring 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", @@ -328,6 +350,7 @@ "install_letsencrypt_cert" : "Install a Let's Encrypt certificate", "manually_renew_letsencrypt_message" : "Certificate will be automatically renewed during the last 15 days of validity. You can manually renew it if you want to. (Not recommended).", "manually_renew_letsencrypt" : "Manually renew now", + "meltdown" : "You are vulnerable to the meltdown critical security vulnerability. To fix that, you need to update your system then reboot it to load the new linux kernel.", "regenerate_selfsigned_cert_message" : "If you want, you can regenerate the self-signed certificate.", "regenerate_selfsigned_cert" : "Regenerate self-signed certificate", "revert_to_selfsigned_cert_message" : "If you really want to, you can reinstall a self-signed certificate. (Not recommended)", @@ -347,4 +370,3 @@ "install_community_appslists_warning" : "Note that these applications packages are not official and not maintained by the YunoHost team.
Installing these applications is at your own risk and could break your system.", "install_custom_app_appslists_info" : "Note that you can use alternative applications lists to install some other apps maintained by the YunoHost community." } - diff --git a/src/locales/eo.json b/src/locales/eo.json index 0967ef42..d15d9873 100644 --- a/src/locales/eo.json +++ b/src/locales/eo.json @@ -1 +1,6 @@ -{} +{ + "password": "Pasvorto", + "login": "Ensaluti", + "logout": "Elsaluti", + "cancel": "Nuligi" +} diff --git a/src/locales/fr.json b/src/locales/fr.json index a7669bb7..caeb33a0 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -296,7 +296,7 @@ "wrong_password": "Mot de passe incorrect", "yes": "Oui", "form_input_example": "Exemple : %s", - "footer_version": "Propulsé par YunoHost %s.", + "footer_version": "Propulsé par YunoHost %s (%s).", "certificate_alert_not_valid": "Critique : le certificat actuel est invalide ! Le HTTPS ne fonctionnera pas du tout !", "certificate_alert_selfsigned": "Attention : le certification actuel est auto-signé. Les navigateurs afficheront une alerte effrayante aux nouveaux visiteurs !", "certificate_alert_letsencrypt_about_to_expire": "Le certificat actuel est sur le point d’expirer. Il doit être renouvelé automatiquement prochainement.", @@ -349,5 +349,22 @@ "name": "Nom", "install_community_appslists_info": "La liste des applications communautaires vous permet d’installer les applications maintenues par la communauté.
Parcourez la liste complète sur yunohost.org/apps_in_progress_fr.", "install_community_appslists_warning": "Ces applications ne sont ni officielles, ni maintenues par l’équipe YunoHost.
Installer ces applications est à vos risques et périls, et peut casser votre système.", - "install_custom_app_appslists_info": "Vous pouvez utiliser des listes alternatives d’applications pour installer d’autres applications maintenues par la communauté YunoHost." + "install_custom_app_appslists_info": "Vous pouvez utiliser des listes alternatives d’applications pour installer d’autres applications maintenues par la communauté YunoHost.", + "domain_dns_conf_is_just_a_recommendation": "Cette page montre la configuration *recommandée*. Elle ne configure *pas* le DNS pour vous. Il est de votre responsabilité que de configurer votre zone DNS chez votre registrar DNS avec cette recommandation.", + "internal_exception": "YunoHost a rencontré une erreur interne :/
Vraiment navré.
Vous devriez chercher de l’aide sur le forum ou le salon pour résoudre le problème, ou rapporter le bogue sur l’outil de suivi.

Les informations suivantes peuvent être utile à l’interlocuteur vous aidant :

Action

%s %s

Trace

%s
", + "confirm_reboot_action_reboot": "Êtes vous sûr de vouloir redémarrer votre serveur ?", + "confirm_reboot_action_shutdown": "Êtes vous sûr de vouloir éteindre votre serveur ?", + "confirm_update_specific_app": "Êtes vous sûr de vouloir mettre à jour %s ?", + "ok": "OK", + "system_upgrade_all_applications_btn": "Mettre à jour toutes les applications", + "system_upgrade_all_packages_btn": "Mettre à jour tous les paquets", + "tools_reboot": "Redémarrer votre serveur", + "tools_reboot_btn": "Redémarrer", + "tools_reboot_done": "Redémarrage...", + "tools_rebooting": "Votre serveur redémarre. Pour retourner sur l'interface d'administration vous devez attendre que votre serveur soit démarré. Pour pouvez vérifier cela en actualisant cette page (F5).", + "tools_shutdown": "Eteindre votre serveur", + "tools_shutdown_btn": "Eteindre", + "tools_shutdown_done": "Arrêt en cours...", + "tools_shuttingdown": "Votre serveur est éteint. Tant que votre serveur est éteint vous ne pouvez plus utiliser l'interface d'administration.", + "tools_shutdown_reboot": "Arrêter/Redémarrer" } diff --git a/src/locales/ru.json b/src/locales/ru.json new file mode 100644 index 00000000..353fe5e5 --- /dev/null +++ b/src/locales/ru.json @@ -0,0 +1,31 @@ +{ + "action": "Действие", + "add": "Добавить", + "administration_password": "Пароль администратора", + "allowed_users": "Разрешенные пользователи", + "api_not_responding": "API не отвечает", + "app_access": "Доступ", + "app_access_addall_btn": "Включить доступ ко всем", + "app_access_addall_desc": "Все существующие пользователи будут иметь доступ к %s.", + "app_access_clearall_btn": "Очистить доступы", + "app_access_clearall_desc": "Каждый пользователь будет иметь доступ к %s.", + "app_access_removeall_btn": "Удалить все доступы", + "app_access_removeall_desc": "Пользователи не получат доступа к %s.", + "app_access_title": "%s доступ", + "app_debug_no_logs": "Журналы приложений недоступны", + "app_debug_tab": "Отображать отладочную информацию", + "app_info_access_desc": "Управление доступом пользователей. Разрешенные пользователи: %s", + "remove": "Удалить", + "app_info_default_desc": "Перенаправить домен root в это приложение (%s).", + "app_info_uninstall_desc": "Удалите это приложение.", + "app_install_cancel": "Установка отменена.", + "app_install_custom_no_manifest": "Нет файла manifest.json", + "app_list": "Нет файла manifest.json", + "app_make_default": "Использовать по умолчанию", + "app_repository": "Происхождение приложения: ", + "app_state": "Состояние приложения: ", + "app_state_inprogress": "Выполняется", + "app_state_validated": "Проверенный", + "app_state_working": "Работает", + "password": "Пароль" +} diff --git a/src/views/app/app_changeurl.ms b/src/views/app/app_changeurl.ms new file mode 100644 index 00000000..e160cca7 --- /dev/null +++ b/src/views/app/app_changeurl.ms @@ -0,0 +1,38 @@ + + +
+ +
+
+
+

{{t 'app_change_url'}}

+
+
+
+ +
+ + {{t 'manage_domains'}} +
+ + +
+ +
+
+
+ +
+ +
+
+
+
diff --git a/src/views/app/app_info.ms b/src/views/app/app_info.ms index 0ae55123..e7cb40b6 100644 --- a/src/views/app/app_info.ms +++ b/src/views/app/app_info.ms @@ -57,6 +57,21 @@
+
+

{{t 'app_info_changeurl_desc' settings.domain}}

+ {{#if change_url}} + + {{t 'app_change_url'}} + + {{else}} + {{#tooltip (t 'app_info_change_url_disabled_tooltip') }} + + {{t 'app_change_url'}} + + {{/tooltip}} + {{/if}} +
+

{{t 'app_info_uninstall_desc'}}

@@ -72,3 +87,4 @@
+{{load_tooltips}} diff --git a/src/views/domain/domain_dns.ms b/src/views/domain/domain_dns.ms index afe0e8a3..e4260837 100644 --- a/src/views/domain/domain_dns.ms +++ b/src/views/domain/domain_dns.ms @@ -4,9 +4,12 @@ {{name}} {{t 'dns'}} -
+
+ {{t 'domain_dns_conf_is_just_a_recommendation' }} +
+

diff --git a/src/views/tools/tools_list.ms b/src/views/tools/tools_list.ms index add62158..3458de9c 100644 --- a/src/views/tools/tools_list.ms +++ b/src/views/tools/tools_list.ms @@ -31,6 +31,10 @@

{{t 'tools_security_feed'}}

+ + +

{{t 'tools_shutdown_reboot'}}

+

{{t 'versions'}}

diff --git a/src/views/tools/tools_reboot.ms b/src/views/tools/tools_reboot.ms new file mode 100644 index 00000000..6786334e --- /dev/null +++ b/src/views/tools/tools_reboot.ms @@ -0,0 +1,28 @@ +
+ +
+ + +
+
+

+ {{t 'operations'}} +

+
+ +
diff --git a/src/views/update/update.ms b/src/views/update/update.ms index 1dd7d6e9..c28e0d4e 100644 --- a/src/views/update/update.ms +++ b/src/views/update/update.ms @@ -26,7 +26,7 @@ {{/packages}}
{{else}}
@@ -42,13 +42,14 @@ {{#if apps}}