diff --git a/customize.dist/pages/features.js b/customize.dist/pages/features.js index 1dfd2417f..bf8115ff6 100644 --- a/customize.dist/pages/features.js +++ b/customize.dist/pages/features.js @@ -13,6 +13,7 @@ define([ return function () { Msg.features_f_apps_note = AppConfig.availablePadTypes.map(function (app) { if (AppConfig.registeredOnlyTypes.indexOf(app) !== -1) { return; } + if (AppConfig.premiumTypes && AppConfig.premiumTypes.includes(app)) { return; } return Msg.type[app]; }).filter(function (x) { return x; }).join(', '); var premiumButton = h('a', { diff --git a/customize.dist/pages/index.js b/customize.dist/pages/index.js index ac37ac104..7e2f980e5 100644 --- a/customize.dist/pages/index.js +++ b/customize.dist/pages/index.js @@ -5,12 +5,13 @@ define([ '/common/common-feedback.js', '/common/common-interface.js', '/common/common-hash.js', + '/common/common-util.js', '/lib/textFit.min.js', '/customize/messages.js', '/customize/application_config.js', '/common/outer/local-store.js', '/customize/pages.js' -], function ($, Config, h, Feedback, UI, Hash, TextFit, Msg, AppConfig, LocalStore, Pages) { +], function ($, Config, h, Feedback, UI, Hash, Util, TextFit, Msg, AppConfig, LocalStore, Pages) { var urlArgs = Config.requireConf.urlArgs; var isAvailableType = function (x) { @@ -18,6 +19,12 @@ define([ return AppConfig.availablePadTypes.indexOf(x) !== -1; }; + + // XXX PREMIUM + var checkPremium = function (x) { + return Util.checkPremiumApp(x, AppConfig.premiumTypes, + LocalStore.getPremium(), LocalStore.isLoggedIn()); + }; var checkRegisteredType = function (x) { // Return true if we're registered or if the app is not registeredOnly if (LocalStore.isLoggedIn()) { return true; } @@ -31,6 +38,8 @@ define([ [ 'code', Msg.type.code], [ 'slide', Msg.type.slide], [ 'sheet', Msg.type.sheet], + [ 'doc', Msg.type.doc], + [ 'presentation', Msg.type.presentation], [ 'form', Msg.type.form], [ 'kanban', Msg.type.kanban], [ 'whiteboard', Msg.type.whiteboard], @@ -40,7 +49,9 @@ define([ }) .map(function (x) { var s = 'div.bs-callout.cp-callout-' + x[0]; + var cls = ''; var isEnabled = checkRegisteredType(x[0]); + var isPremium = checkPremium(x[0]); //if (i > 2) { s += '.cp-more.cp-hidden'; } var icon = AppConfig.applicationsIcon[x[0]]; var font = icon.indexOf('cptools') === 0 ? 'cptools' : 'fa'; @@ -52,11 +63,16 @@ define([ window.location.href = url; } }; + if (isPremium === -1) { + cls += '.cp-app-hidden.cp-app-disabled'; + } else if (isPremium === 0) { + cls += '.cp-app-disabled'; + } if (!isEnabled) { - s += '.cp-app-disabled'; + cls += '.cp-app-disabled'; attr.title = Msg.mustLogin; } - return h('a', [ + return h('a.cp-index-appitem' + cls, [ attr, h(s, [ h('i.' + font + '.' + icon, {'aria-hidden': 'true'}), diff --git a/customize.dist/src/less2/include/contextmenu.less b/customize.dist/src/less2/include/contextmenu.less index 09b26dda9..1c6912f6e 100644 --- a/customize.dist/src/less2/include/contextmenu.less +++ b/customize.dist/src/less2/include/contextmenu.less @@ -59,6 +59,11 @@ color: @cp_context-icon; width: 16px; } + // XXX PREMIUM + &.cp-app-disabled { + cursor: not-allowed !important; + opacity: 0.5; + } } } .cp-app-drive-context-noAction { diff --git a/customize.dist/src/less2/include/drive.less b/customize.dist/src/less2/include/drive.less index 5feb22ca3..9e2cb76b8 100644 --- a/customize.dist/src/less2/include/drive.less +++ b/customize.dist/src/less2/include/drive.less @@ -80,6 +80,15 @@ display: none; } } + + // XXX PREMIUM + &.cp-app-hidden { + display: none; + } + &.cp-app-disabled { + cursor: not-allowed !important; + opacity: 0.5; + } } } diff --git a/customize.dist/src/less2/include/dropdown.less b/customize.dist/src/less2/include/dropdown.less index ddba87cd7..a7c810692 100644 --- a/customize.dist/src/less2/include/dropdown.less +++ b/customize.dist/src/less2/include/dropdown.less @@ -120,6 +120,15 @@ background-color: @cp_dropdown-bg-active; color: @cp_dropdown-fg; } + + // XXX PREMIUM + &.cp-app-hidden { + display: none; + } + &.cp-app-disabled { + cursor: not-allowed !important; + opacity: 0.5; + } } &> span { box-sizing: border-box; diff --git a/customize.dist/src/less2/include/icons.less b/customize.dist/src/less2/include/icons.less index 6c05803c7..501980c83 100644 --- a/customize.dist/src/less2/include/icons.less +++ b/customize.dist/src/less2/include/icons.less @@ -38,5 +38,14 @@ display: none; } } + + // XXX PREMIUM + &.cp-app-hidden { + display: none; + } + &.cp-app-disabled { + cursor: not-allowed !important; + opacity: 0.5; + } } } diff --git a/customize.dist/src/less2/pages/page-index.less b/customize.dist/src/less2/pages/page-index.less index 8c5fdc164..41a3014c7 100644 --- a/customize.dist/src/less2/pages/page-index.less +++ b/customize.dist/src/less2/pages/page-index.less @@ -118,15 +118,23 @@ padding: 10px 10px 0px 10px; //height: @icons-size - @icons-text-size; } - &.cp-app-disabled { - cursor: not-allowed !important; - opacity: 0.5; - } .pad-button-text { color: @cryptpad_text_col; padding: 5px; } } + .cp-index-appitem { + // XXX PREMIUM + &.cp-app-hidden { + display: none; + } + &.cp-app-disabled { + div { + cursor: not-allowed !important; + } + opacity: 0.5; + } + } h4 { margin: 0; } diff --git a/www/common/common-constants.js b/www/common/common-constants.js index fd4d1474b..5f0f59be6 100644 --- a/www/common/common-constants.js +++ b/www/common/common-constants.js @@ -11,6 +11,7 @@ define(['/customize/application_config.js'], function (AppConfig) { storageKey: 'filesData', tokenKey: 'loginToken', prefersDriveRedirectKey: 'prefersDriveRedirect', + isPremiumKey: 'isPremiumUser', displayPadCreationScreen: 'displayPadCreationScreen', deprecatedKey: 'deprecated', MAX_TEAMS_SLOTS: AppConfig.maxTeamsSlots || 5, diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 4be00efec..f7ad0220a 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -2087,6 +2087,7 @@ define([ }; + UIElements.createNewPadModal = function (common) { // if in drive, show new pad modal instead if ($(".cp-app-drive-element-row.cp-app-drive-new-ghost").length !== 0) { @@ -2113,6 +2114,9 @@ define([ AppConfig.registeredOnlyTypes.indexOf(p) !== -1) { return; } return true; }); + + var priv = common.getMetadataMgr().getPrivateData(); + types.forEach(function (p) { var $element = $('
  • ', { 'class': 'cp-icons-element', @@ -2125,6 +2129,13 @@ define([ $modal.hide(); common.openURL('/' + p + '/'); }); + // XXX PREMIUM + var premium = Util.checkPremiumApp(p, AppConfig.premiumTypes, priv.plan, priv.loggedIn); + if (premium === -1) { + $element.addClass('cp-app-hidden cp-app-disabled'); + } else if (premium === 0) { + $element.addClass('cp-app-disabled'); + } }); var selected = -1; diff --git a/www/common/common-util.js b/www/common/common-util.js index e70f4747e..47359232b 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -638,6 +638,17 @@ getColor().toString(16); }; + Util.checkPremiumApp = function (app, premiumTypes, plan, loggedIn) { + // If this is not a premium app, don't disable it + if (!Array.isArray(premiumTypes) || !premiumTypes.includes(app)) { return 2; } + // This is a premium app + // if you're not logged in, disbale it + if (!loggedIn) { return -1; } + // if you're logged in, enable it only if you're a premium user + return plan ? 1 : 0; + + }; + /* Chrome 92 dropped support for SharedArrayBuffer in cross-origin contexts where window.crossOriginIsolated is false. diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 165ac7b02..dfcfe6da2 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -338,7 +338,11 @@ define([ Messages.fc_openInSheet = "Edit in Sheet"; // XXX Messages.fc_openInDoc = "Edit in Document"; // XXX Messages.fc_openInPresentation = "Edit in Presentation"; // XXX - var createContextMenu = function () { + var createContextMenu = function (priv) { + // XXX PREMIUM + // XXX "Edit in Document" and "New Document" (and presentation) + var premiumP = Util.checkPremiumApp('presentation', AppConfig.premiumTypes, priv.plan, priv.loggedIn); + var premiumD = Util.checkPremiumApp('doc', AppConfig.premiumTypes, priv.plan, priv.loggedIn); var menu = h('div.cp-contextmenu.dropdown.cp-unselectable', [ h('ul.dropdown-menu', { 'role': 'menu', @@ -366,11 +370,11 @@ define([ 'tabindex': '-1', 'data-icon': faOpenInSheet, }, Messages.fc_openInSheet)), - h('li', h('a.cp-app-drive-context-openindoc.dropdown-item', { + premiumD === -1 ? undefined : h('li', h('a.cp-app-drive-context-openindoc.dropdown-item' + (premiumD === 0 ? '.cp-app-disabled' : ''), { 'tabindex': '-1', 'data-icon': faOpenInDoc, }, Messages.fc_openInDoc)), - h('li', h('a.cp-app-drive-context-openinpresentation.dropdown-item', { + premiumP === -1 ? undefined : h('li', h('a.cp-app-drive-context-openinpresentation.dropdown-item' + (premiumP === 0 ? '.cp-app-disabled' : ''), { 'tabindex': '-1', 'data-icon': faOpenInPresentation, }, Messages.fc_openInPresentation)), @@ -446,6 +450,16 @@ define([ 'data-icon': AppConfig.applicationsIcon.sheet, 'data-type': 'sheet' }, Messages.button_newsheet)), + premiumD === -1 ? undefined : h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' + (premiumD === 0 ? '.cp-app-disabled' : ''), { + 'tabindex': '-1', + 'data-icon': AppConfig.applicationsIcon.doc, + 'data-type': 'doc' + }, Messages.button_newdoc)), + premiumP === -1 ? undefined : h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' + (premiumP === 0 ? '.cp-app-disabled' : ''), { + 'tabindex': '-1', + 'data-icon': AppConfig.applicationsIcon.presentation, + 'data-type': 'presentation' + }, Messages.button_newpresentation)), h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', { 'tabindex': '-1', 'data-icon': AppConfig.applicationsIcon.whiteboard, @@ -631,7 +645,7 @@ define([ var $content = APP.$content = $("#cp-app-drive-content"); var $appContainer = $(".cp-app-drive-container"); var $driveToolbar = APP.toolbar.$bottom; - var $contextMenu = createContextMenu().appendTo($appContainer); + var $contextMenu = createContextMenu(priv).appendTo($appContainer); var $contentContextMenu = $("#cp-app-drive-context-content"); var $defaultContextMenu = $("#cp-app-drive-context-default"); @@ -2929,6 +2943,15 @@ define([ 'data-type': type, 'href': '#' }; + + // XXX PREMIUM + var premium = Util.checkPremiumApp(type, AppConfig.premiumTypes, priv.plan, priv.loggedIn); + if (premium === -1) { + attributes.class += ' cp-app-hidden cp-app-disabled'; + } else if (premium === 0) { + attributes.class += ' cp-app-disabled'; + } + options.push({ tag: 'a', attributes: attributes, @@ -3255,6 +3278,14 @@ define([ $element.append($('', {'class': 'cp-app-drive-new-name'}) .text(Messages.type[type])); $element.attr('data-type', type); + + // XXX PREMIUM + var premium = Util.checkPremiumApp(type, AppConfig.premiumTypes, priv.plan, priv.loggedIn); + if (premium === -1) { + $element.addClass('cp-app-hidden cp-app-disabled'); + } else if (premium === 0) { + $element.addClass('cp-app-disabled'); + } }); $container.find('.cp-app-drive-element-row').click(function () { diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 77d36645c..5d6f2e0ff 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -90,6 +90,15 @@ define([ localStorage.setItem(Constants.redirectToDriveKey, Boolean(bool)); }; + LocalStore.getPremium = function () { + try { + return JSON.parse(localStorage[Constants.isPremiumKey]); + } catch (err) { return; } + }; + LocalStore.setPremium = function (bool) { + localStorage.setItem(Constants.isPremiumKey, Boolean(bool)); + }; + LocalStore.login = function (hash, name, cb) { if (!hash) { throw new Error('expected a user hash'); } if (!name) { throw new Error('expected a user name'); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index bce02657b..f90728ade 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -664,6 +664,13 @@ define([ additionalPriv.registeredOnly = true; } + // XXX PREMIUM + var priv = metaObj.priv; + var p = Utils.Util.checkPremiumApp(parsed.type, AppConfig.premiumTypes, priv.plan, additionalPriv.loggedIn); + if (p === 0 || p === -1) { + additionalPriv.premiumOnly = true; + } + if (isSafe) { additionalPriv.hashes = hashes; additionalPriv.password = password; @@ -675,6 +682,10 @@ define([ cfg.addData(metaObj.priv, Cryptpad, metaObj.user, Utils); } + if (metaObj && metaObj.priv && typeof(metaObj.priv.plan) === "string") { + Utils.LocalStore.setPremium(metaObj.priv.plan); + } + sframeChan.event('EV_METADATA_UPDATE', metaObj); }); }; diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index b7efd9f4f..ecd4f094e 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -907,6 +907,15 @@ define([ }, {forefront: true}); return; } + // XXX PREMIUM + Messages.premiumOnly = "Premium only for now..."; // XXX + var blocked = privateData.premiumOnly && privateData.isNewFile; + if (blocked) { + UI.alert(Messages.premiumOnly, function () { + funcs.gotoURL('/drive/'); + }, {forefront: true}); + return; + } } catch (e) { console.error("Can't check permissions for the app"); }