From a2731c44b09e53a3f794cd057b636fa483d75e62 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 6 May 2022 13:55:00 +0530 Subject: [PATCH] assorted home page fixes/improvements * link to home page from other static pages * home notice * moved to the top of the home page * configured via server API instead of application_config.js * WIP admin panel UI * more debugging info for unsupported decrees * lint compliance probably --- customize.dist/pages.js | 10 +++++-- customize.dist/pages/index.js | 20 ++++++-------- lib/commands/admin-rpc.js | 1 + lib/decrees.js | 39 +++++++++++++++++++++++--- lib/env.js | 7 +++-- server.js | 13 +++------ www/admin/app-admin.less | 1 + www/admin/inner.js | 52 ++++++++++++++++++++++++++++++++--- 8 files changed, 110 insertions(+), 33 deletions(-) diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 47fa920f8..370043368 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -74,6 +74,7 @@ define([ return select; }; +/* // XXX remove ? var footerCol = function (title, L, n) { n = n || 3; return h('div.col-sm-' + n, [ @@ -87,6 +88,7 @@ define([ ) ]); }; +*/ var footLink = function (ref, loc, text, icon) { if (!ref) { return; } @@ -243,6 +245,11 @@ define([ // $('#menuCollapse').slideDown(); // }); + var isHome = ['/', '/index.html'].includes(window.location.pathname); + var homeLink = h('a.nav-item.nav-link' /* .navbar-brand */, { href: '/index.html' }, [ + 'Home page', // XXX replace with image or whatever + ]); + return h('nav.navbar.navbar-expand-lg', // XXX DB add link back to index.html on other pages // h('a.navbar-brand', { href: '/index.html'}, [ @@ -255,8 +262,7 @@ define([ // button, // XXX collapse button // add .collapse.navbar-collapse.justify-content-end#menuCollapse to div below to enable collapse button [ - // XXX remove about page - // h('a.nav-item.nav-link', { href: '/what-is-cryptpad.html'}, Msg.about), + !isHome? homeLink: undefined, h('a.nav-item.nav-link', { href: '/features.html'}, Pages.areSubscriptionsAllowed()? Msg.pricing: Msg.features), h('a.nav-item.nav-link', { href: 'https://docs.cryptpad.fr'}, [h('i.fa.fa-book', {'aria-hidden':'true'}),Msg.docs_link]), diff --git a/customize.dist/pages/index.js b/customize.dist/pages/index.js index f259ae5fb..b917d66ec 100644 --- a/customize.dist/pages/index.js +++ b/customize.dist/pages/index.js @@ -109,7 +109,8 @@ define([ var fastLink = k => pageLink(Pages.customURLs[k], k); // XXX DB - Msg.terms = "Terms of Service"; + Msg.terms = Msg.footer_tos; //"Terms of Service"; // XXX + Msg.home_location = "Encrypted data is hosted in {0}"; // XXX // XXX DB: this may be wrong, pasted over form pages.js var imprintLink = fastLink('imprint'); @@ -121,10 +122,9 @@ define([ If the text is the key for the translation system then then the most appropriate translated text will be displayed. Otherwise, the direct text will be included as HTML. */ - if (AppConfig.homeNotice) { - notice = h('div.alert.alert-info', Pages.setHTML(h('span'), [ - Msg[AppConfig.homeNotice] || AppConfig.homeNotice - ])); + if (Pages.Instance.notice) { + console.log(Pages.Instance.notice); + notice = h('div.alert.alert-info', Pages.setHTML(h('span'), Pages.Instance.notice)); } // instance title @@ -134,15 +134,13 @@ define([ // instance location var locationBlock; - if (Pages.Instance.location) { locationBlock = h('div.cp-instance-location', [ h('i.fa.fa-map-pin', {'aria-hidden': 'true'}), - 'Encrypted data is hosted in: ', // XXX translate - Pages.Instance.location, + Msg._getKey('home_location', [ Pages.Instance.location ]), ]); } else { - locationBlock = h('div', h('br')); // XXX + locationBlock = h('div', h('br')); } Msg.home_morestorage = 'For more storage space, and to support the project:'; // XXX @@ -165,6 +163,7 @@ define([ return [ h('div#cp-main', [ Pages.infopageTopbar(), + notice, h('div.container.cp-container', [ h('div.row.cp-home-hero', [ h('div.cp-title.col-md-6', [ @@ -174,7 +173,7 @@ define([ alt: '' }), instanceTitle, - UI.setHTML(h('span.tag-line'), Pages.Instance.description), + Pages.setHTML(h('span.tag-line'), Pages.Instance.description), locationBlock, termsLink, privacyLink, @@ -199,7 +198,6 @@ define([ ]) ]) ]), - notice ]), Pages.infopageFooter(), ]), diff --git a/lib/commands/admin-rpc.js b/lib/commands/admin-rpc.js index 57b91c8d6..a291cd969 100644 --- a/lib/commands/admin-rpc.js +++ b/lib/commands/admin-rpc.js @@ -359,6 +359,7 @@ var instanceStatus = function (Env, Server, cb) { instanceDescription: Env.instanceDescription, instanceJurisdiction: Env.instanceJurisdiction, instanceName: Env.instanceName, + instanceNotice: Env.instanceNotice, }); }; diff --git a/lib/decrees.js b/lib/decrees.js index 86664826f..d5aadd860 100644 --- a/lib/decrees.js +++ b/lib/decrees.js @@ -196,14 +196,45 @@ commands.SET_SUPPORT_MAILBOX = makeGenericSetter('supportMailbox', function (arg // CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_PURPOSE', ["development"]]], console.log) commands.SET_INSTANCE_PURPOSE = makeGenericSetter('instancePurpose', args_isString); +var makeTranslation = function (attr) { + return function (Env, args) { + if (!Array.isArray(args)) { throw new Error("INVALID_ARGS"); } + var value = args[0]; + var state = Env[attr]; + + if (typeof(value) === 'string') { + if (state.default === value) { return false; } + state.default = value; + return true; + } + if (value && typeof(value) === 'object') { + var changed = false; + Object.keys(value).forEach(function (lang) { + if (state[lang] === value[lang]) { return; } + state[lang] = value[lang]; + changed = true; + }); + return changed; + } + return false; + }; +}; + // CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_JURISDICTION', ['France']]], console.log) -commands.SET_INSTANCE_JURISDICTION = makeGenericSetter('instanceJurisdiction', args_isString); +// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_JURISDICTION', [{default:'France',de:'Frankreich'}]]], console.log) +commands.SET_INSTANCE_JURISDICTION = makeTranslation('instanceJurisdiction'); // CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_NAME', ['My Personal CryptPad']]], console.log) -commands.SET_INSTANCE_NAME = makeGenericSetter('instanceName', args_isString); +// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_NAME', [{default:'My Personal CryptPad', fr: "Mon CryptPad personnel"}]]], console.log) +commands.SET_INSTANCE_NAME = makeTranslation('instanceName'); // CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_DESCRIPTION', ['A personal instance, hosted for me and nobody else']]], console.log) -commands.SET_INSTANCE_DESCRIPTION = makeGenericSetter('instanceDescription', args_isString); +// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_DESCRIPTION', [{default:'A personal server, not intended for public usage', fr: 'Un serveur personnel, non destiné à un usage public'}]]], console.log) +commands.SET_INSTANCE_DESCRIPTION = makeTranslation('instanceDescription'); // XXX support translation + +// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_NOTICE', ['Our hosting costs have increased during the pandemic. Please consider donating!']]], console.log) +// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_NOTICE', [{default:'Our hosting costs have increased during the pandemic. Please consider donating!',fr:'Nos coûts d'hébergement ont augmenté pendant la pandémie. Veuillez envisager de faire un don !']]], console.log) +commands.SET_INSTANCE_NOTICE = makeTranslation('instanceNotice'); // Maintenance: Empty string or an object with a start and end time var isNumber = function (value) { @@ -370,7 +401,7 @@ Decrees.load = function (Env, _cb) { try { handler(void 0, JSON.parse(text)); } catch (err) { - handler(err); + handler(err, text); } next(); }, function (err) { diff --git a/lib/env.js b/lib/env.js index d9c22feaf..ad5aff081 100644 --- a/lib/env.js +++ b/lib/env.js @@ -192,9 +192,10 @@ module.exports.create = function (config) { provideAggregateStatistics: false, updateAvailable: undefined, - instanceName: '', - instanceDescription: '', - instanceJurisdiction: '', + instanceName: {}, + instanceDescription: {}, + instanceJurisdiction: {}, + instanceNotice: {}, myDomain: config.myDomain, mySubdomain: config.mySubdomain, // only exists for the accounts integration diff --git a/server.js b/server.js index cd48835d3..a9bebabf4 100644 --- a/server.js +++ b/server.js @@ -270,15 +270,10 @@ var define = function (obj) { app.get('/api/instance', function (req, res) { // XXX use caching? res.setHeader('Content-Type', 'text/javascript'); res.send(define({ - name: { - default: Env.instanceName, - }, - description: { - default: Env.instanceDescription, - }, - location: { - default: Env.instanceJurisdiction, - }, + name: Env.instanceName, + description: Env.instanceDescription, + location: Env.instanceJurisdiction, + notice: Env.instanceNotice, })); }); diff --git a/www/admin/app-admin.less b/www/admin/app-admin.less index 4f0737cef..d74fe9cb3 100644 --- a/www/admin/app-admin.less +++ b/www/admin/app-admin.less @@ -40,6 +40,7 @@ } .cp-admin-setlimit-form, .cp-admin-setjurisdiction-form, + .cp-admin-setnotice-form, // XXX not great .cp-admin-setdescription-form { + button { margin-top: 5px !important; diff --git a/www/admin/inner.js b/www/admin/inner.js index 48a8d6c33..57f39fe16 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -62,6 +62,7 @@ define([ 'cp-admin-name', 'cp-admin-description', 'cp-admin-jurisdiction', + 'cp-admin-notice', ], 'quota': [ // Msg.admin_cat_quota 'cp-admin-defaultlimit', @@ -429,7 +430,7 @@ define([ return $div; }; - create['jurisdiction'] = function () { + create['jurisdiction'] = function () { // XXX make translateable var key = 'jurisdiction'; var $div = makeBlock(key, true); // Msg.admin_jurisdictionHint, Msg.admin_jurisdictionTitle, Msg.admin_jurisdictionButton var $button = $div.find('button').addClass('cp-listing-action').text(Messages.settings_save); @@ -468,6 +469,49 @@ define([ return $div; }; + Messages.admin_noticeTitle = "admin_noticeTitle"; + Messages.admin_noticeHint = "admin_noticeHint"; + //Messages.admin_noticeButton = "admin_noticeButton"; + + create['notice'] = function () { // XXX add input, make translateable + var key = 'notice'; + var $div = makeBlock(key, true); + + var $button = $div.find('button').addClass('cp-listing-action').text(Messages.settings_save); + + var input = h('input.cp-listing-info', { + type: 'text', + value: APP.instanceStatus.instanceNotice || '', + placeholder: Messages.owner_unknownUser || '', + }); + var $input = $(input); + var innerDiv = h('div.cp-admin-setnotice-form', input); + var spinner = UI.makeSpinner($(innerDiv)); + + $button.click(function () { + spinner.spin(); + $button.attr('disabled', 'disabled'); + sFrameChan.query('Q_ADMIN_RPC', { + cmd: 'ADMIN_DECREE', + data: ['SET_INSTANCE_NOTICE', [$input.val().trim()]] + }, function (e, response) { + $button.removeAttr('disabled'); + spinner.hide(); + if (e || response.error) { + UI.warn(Messages.error); + $input.val(''); + console.error(e, response); + return; + } + UI.log(Messages._getKey('ui_saved', [Messages.admin_noticeTitle])); + }); + }); + + $button.before(innerDiv); + + return $div; + }; + create['instance-info-notice'] = function () { return $(h('div.cp-admin-instance-info-notice.cp-sidebarlayout-element', h('div.alert.alert-info.cp-admin-bigger-alert', [ @@ -478,7 +522,7 @@ define([ )); }; - create['name'] = function () { + create['name'] = function () { // XXX make translateable var key = 'name'; var $div = makeBlock(key, true); // Msg.admin_nameHint, Msg.admin_nameTitle, Msg.admin_nameButton @@ -519,7 +563,7 @@ define([ return $div; }; - create['description'] = function () { + create['description'] = function () { // XXX support translation var key = 'description'; var $div = makeBlock(key, true); // Msg.admin_descriptionHint @@ -1419,7 +1463,7 @@ define([ }; - create['broadcast'] = function () { + create['broadcast'] = function () { // XXX var key = 'broadcast'; var $div = makeBlock(key); // Msg.admin_broadcastHint, admin_broadcastTitle