diff options
Diffstat (limited to 'trunk/etherpad/src/etherpad/control/pro')
8 files changed, 1891 insertions, 0 deletions
diff --git a/trunk/etherpad/src/etherpad/control/pro/account_control.js b/trunk/etherpad/src/etherpad/control/pro/account_control.js new file mode 100644 index 0000000..031dbe6 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/account_control.js @@ -0,0 +1,369 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("stringutils"); +import("stringutils.*"); +import("funhtml.*"); +import("email.sendEmail"); +import("cache_utils.syncedWithCache"); + +import("etherpad.helpers"); +import("etherpad.utils.*"); +import("etherpad.sessions.getSession"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_account_auto_signin"); +import("etherpad.pro.pro_config"); +import("etherpad.pad.pad_security"); +import("etherpad.pad.padutils"); +import("etherpad.pad.padusers"); +import("etherpad.collab.collab_server"); + +function onRequest() { + if (!getSession().tempFormData) { + getSession().tempFormData = {}; + } + + return false; // path not handled here +} + +//-------------------------------------------------------------------------------- +// helpers +//-------------------------------------------------------------------------------- + +function _redirOnError(m, clearQuery) { + if (m) { + getSession().accountFormError = m; + + var dest = request.url; + if (clearQuery) { + dest = request.path; + } + response.redirect(dest); + } +} + +function setSigninNotice(m) { + getSession().accountSigninNotice = m; +} + +function setSessionError(m) { + getSession().accountFormError = m; +} + +function _topDiv(id, name) { + var m = getSession()[name]; + if (m) { + delete getSession()[name]; + return DIV({id: id}, m); + } else { + return ''; + } +} + +function _messageDiv() { return _topDiv('account-message', 'accountMessage'); } +function _errorDiv() { return _topDiv('account-error', 'accountFormError'); } +function _signinNoticeDiv() { return _topDiv('signin-notice', 'accountSigninNotice'); } + +function _renderTemplate(name, data) { + data.messageDiv = _messageDiv; + data.errorDiv = _errorDiv; + data.signinNotice = _signinNoticeDiv; + data.tempFormData = getSession().tempFormData; + renderFramed('pro/account/'+name+'.ejs', data); +} + +//---------------------------------------------------------------- +// /ep/account/ +//---------------------------------------------------------------- + +function render_main_get() { + _renderTemplate('my-account', { + account: getSessionProAccount(), + changePass: getSession().changePass + }); +} + +function render_update_info_get() { + response.redirect('/ep/account/'); +} + +function render_update_info_post() { + var fullName = request.params.fullName; + var email = trim(request.params.email); + + getSession().tempFormData.email = email; + getSession().tempFormData.fullName = fullName; + + _redirOnError(pro_accounts.validateEmail(email)); + _redirOnError(pro_accounts.validateFullName(fullName)); + + pro_accounts.setEmail(getSessionProAccount(), email); + pro_accounts.setFullName(getSessionProAccount(), fullName); + + getSession().accountMessage = "Info updated."; + response.redirect('/ep/account/'); +} + +function render_update_password_get() { + response.redirect('/ep/account/'); +} + +function render_update_password_post() { + var password = request.params.password; + var passwordConfirm = request.params.passwordConfirm; + + if (password != passwordConfirm) { _redirOnError('Passwords did not match.'); } + + _redirOnError(pro_accounts.validatePassword(password)); + + pro_accounts.setPassword(getSessionProAccount(), password); + + if (getSession().changePass) { + delete getSession().changePass; + response.redirect('/'); + } + + getSession().accountMessage = "Password updated."; + response.redirect('/ep/account/'); +} + +//-------------------------------------------------------------------------------- +// signin/signout +//-------------------------------------------------------------------------------- + +function render_sign_in_get() { + if (request.params.uid && request.params.tp) { + var m = pro_accounts.authenticateTempSignIn(Number(request.params.uid), request.params.tp); + if (m) { + getSession().accountFormError = m; + response.redirect('/ep/account/'); + } + } + if (request.params.instantSigninKey) { + _attemptInstantSignin(request.params.instantSigninKey); + } + if (getSession().recentlySignedOut && getSession().accountFormError) { + delete getSession().accountFormError; + delete getSession().recentlySignedOut; + } + // Note: must check isAccountSignedIn before calling checkAutoSignin()! + if (pro_accounts.isAccountSignedIn()) { + _redirectToPostSigninDestination(); + } + pro_account_auto_signin.checkAutoSignin(); + var domainRecord = domains.getRequestDomainRecord(); + var showGuestBox = false; + if (request.params.guest && request.params.padId) { + showGuestBox = true; + } + _renderTemplate('signin', { + domain: pro_utils.getFullProDomain(), + siteName: toHTML(pro_config.getConfig().siteName), + email: getSession().tempFormData.email || "", + password: getSession().tempFormData.password || "", + rememberMe: getSession().tempFormData.rememberMe || false, + showGuestBox: showGuestBox, + localPadId: request.params.padId + }); +} + +function _attemptInstantSignin(key) { + // See src/etherpad/control/global_pro_account_control.js + var email = null; + var password = null; + syncedWithCache('global_signin_passwords', function(c) { + if (c[key]) { + email = c[key].email; + password = c[key].password; + } + delete c[key]; + }); + getSession().tempFormData.email = email; + _redirOnError(pro_accounts.authenticateSignIn(email, password), true); +} + +function render_sign_in_post() { + var email = trim(request.params.email); + var password = request.params.password; + + getSession().tempFormData.email = email; + getSession().tempFormData.rememberMe = request.params.rememberMe; + + _redirOnError(pro_accounts.authenticateSignIn(email, password)); + pro_account_auto_signin.setAutoSigninCookie(request.params.rememberMe); + _redirectToPostSigninDestination(); +} + +function render_guest_sign_in_get() { + var localPadId = request.params.padId; + var domainId = domains.getRequestDomainId(); + var globalPadId = padutils.makeGlobalId(domainId, localPadId); + var userId = padusers.getUserId(); + + pro_account_auto_signin.checkAutoSignin(); + pad_security.clearKnockStatus(userId, globalPadId); + + _renderTemplate('signin-guest', { + localPadId: localPadId, + errorMessage: getSession().guestAccessError, + siteName: toHTML(pro_config.getConfig().siteName), + guestName: padusers.getUserName() || "" + }); +} + +function render_guest_sign_in_post() { + function _err(m) { + if (m) { + getSession().guestAccessError = m; + response.redirect(request.url); + } + } + var displayName = request.params.guestDisplayName; + var localPadId = request.params.localPadId; + if (!(displayName && displayName.length > 0)) { + _err("Please enter a display name"); + } + getSession().guestDisplayName = displayName; + response.redirect('/ep/account/guest-knock?padId='+encodeURIComponent(localPadId)+ + "&guestDisplayName="+encodeURIComponent(displayName)); +} + +function render_guest_knock_get() { + var localPadId = request.params.padId; + helpers.addClientVars({ + localPadId: localPadId, + guestDisplayName: request.params.guestDisplayName, + padUrl: "http://"+httpHost(request.host)+"/"+localPadId + }); + _renderTemplate('guest-knock', {}); +} + +function render_guest_knock_post() { + var localPadId = request.params.padId; + var displayName = request.params.guestDisplayName; + var domainId = domains.getRequestDomainId(); + var globalPadId = padutils.makeGlobalId(domainId, localPadId); + var userId = padusers.getUserId(); + + response.setContentType("text/plain; charset=utf-8"); + // has the knock already been answsered? + var currentAnswer = pad_security.getKnockAnswer(userId, globalPadId); + if (currentAnswer) { + response.write(currentAnswer); + } else { + collab_server.guestKnock(globalPadId, userId, displayName); + response.write("wait"); + } +} + +function _redirectToPostSigninDestination() { + var cont = request.params.cont; + if (!cont) { cont = '/'; } + response.redirect(cont); +} + +function render_sign_out() { + pro_account_auto_signin.setAutoSigninCookie(false); + pro_accounts.signOut(); + delete getSession().padPasswordAuth; + getSession().recentlySignedOut = true; + response.redirect("/"); +} + +//-------------------------------------------------------------------------------- +// create-admin-account (eepnet only) +//-------------------------------------------------------------------------------- + +function render_create_admin_account_get() { + if (pro_accounts.doesAdminExist()) { + renderFramedError("An admin account already exists on this domain."); + response.stop(); + } + _renderTemplate('create-admin-account', {}); +} + +function render_create_admin_account_post() { + var email = trim(request.params.email); + var password = request.params.password; + var passwordConfirm = request.params.passwordConfirm; + var fullName = request.params.fullName; + + getSession().tempFormData.email = email; + getSession().tempFormData.fullName = fullName; + + if (password != passwordConfirm) { _redirOnError('Passwords did not match.'); } + + _redirOnError(pro_accounts.validateEmail(email)); + _redirOnError(pro_accounts.validateFullName(fullName)); + _redirOnError(pro_accounts.validatePassword(password)); + + pro_accounts.createNewAccount(null, fullName, email, password, true); + + var u = pro_accounts.getAccountByEmail(email, null); + + // TODO: should we send a welcome email here? + //pro_accounts.sendWelcomeEmail(u); + + _redirOnError(pro_accounts.authenticateSignIn(email, password)); + + response.redirect("/"); +} + + +//-------------------------------------------------------------------------------- +// forgot password +//-------------------------------------------------------------------------------- + +function render_forgot_password_get() { + if (request.params.instantSubmit && request.params.email) { + render_forgot_password_post(); + } else { + _renderTemplate('forgot-password', { + email: getSession().tempFormData.email || "" + }); + } +} + +function render_forgot_password_post() { + var email = trim(request.params.email); + + getSession().tempFormData.email = email; + + var u = pro_accounts.getAccountByEmail(email, null); + if (!u) { + _redirOnError("Account not found: "+email); + } + + var tempPass = stringutils.randomString(10); + pro_accounts.setTempPassword(u, tempPass); + + var subj = "EtherPad: Request to reset your password on "+request.domain; + var body = renderTemplateAsString('pro/account/forgot-password-email.ejs', { + account: u, + recoverUrl: pro_accounts.getTempSigninUrl(u, tempPass) + }); + var fromAddr = pro_utils.getEmailFromAddr(); + sendEmail(u.email, fromAddr, subj, {}, body); + + getSession().accountMessage = "An email has been sent to "+u.email+" with instructions to reset the password."; + response.redirect(request.path); +} + + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/account_manager_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/account_manager_control.js new file mode 100644 index 0000000..8f93b2e --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/account_manager_control.js @@ -0,0 +1,260 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("funhtml.*"); +import("stringutils"); +import("stringutils.*"); +import("email.sendEmail"); + +import("etherpad.globals.*"); +import("etherpad.utils.*"); +import("etherpad.sessions.getSession"); + +import("etherpad.control.pro.admin.pro_admin_control"); + +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.pro_utils"); +import("etherpad.pro.pro_config"); +import("etherpad.pro.domains"); +import("etherpad.billing.team_billing"); + +jimport("java.lang.System.out.println"); + +function _err(m) { + if (m) { + getSession().accountManagerError = m; + response.redirect(request.path); + } +} + +function _renderTopDiv(mid, htmlId) { + var m = getSession()[mid]; + if (m) { + delete getSession()[mid]; + return DIV({id: htmlId}, m); + } else { + return ''; + } +} + +function _errorDiv() { return _renderTopDiv('accountManagerError', 'error-message'); } +function _messageDiv() { return _renderTopDiv('accountManagerMessage', 'message'); } +function _warningDiv() { return _renderTopDiv('accountManagerWarning', 'warning'); } + +function onRequest() { + var parts = request.path.split('/'); + + function dispatchAccountAction(action, handlerGet, handlerPost) { + if ((parts[4] == action) && (isNumeric(parts[5]))) { + if (request.isGet) { handlerGet(+parts[5]); } + if (request.isPost) { handlerPost(+parts[5]); } + return true; + } + return false; + } + + if (dispatchAccountAction('account', render_account_get, render_account_post)) { + return true; + } + if (dispatchAccountAction('delete-account', render_delete_account_get, render_delete_account_post)) { + return true; + }; + + return false; +} + +function render_main() { + var accountList = pro_accounts.listAllDomainAccounts(); + pro_admin_control.renderAdminPage('account-manager', { + accountList: accountList, + messageDiv: _messageDiv, + warningDiv: _warningDiv + }); +} + +function render_new_get() { + pro_admin_control.renderAdminPage('new-account', { + oldData: getSession().accountManagerFormData || {}, + stringutils: stringutils, + errorDiv: _errorDiv + }); +} + +function _ensureBillingOK() { + var activeAccounts = pro_accounts.getCachedActiveCount(domains.getRequestDomainId()); + if (activeAccounts < PRO_FREE_ACCOUNTS) { + return; + } + + var status = team_billing.getDomainStatus(domains.getRequestDomainId()); + if (!((status == team_billing.CURRENT) + || (status == team_billing.PAST_DUE))) { + _err(SPAN( + "A payment profile is required to create more than ", PRO_FREE_ACCOUNTS, + " accounts. ", + A({href: "/ep/admin/billing/", id: "billinglink"}, "Manage billing"))); + } +} + +function render_new_post() { + if (request.params.cancel) { + response.redirect('/ep/admin/account-manager/'); + } + + _ensureBillingOK(); + + var fullName = request.params.fullName; + var email = trim(request.params.email); + var tempPass = request.params.tempPass; + var makeAdmin = !!request.params.makeAdmin; + + getSession().accountManagerFormData = { + fullName: fullName, + email: email, + tempPass: tempPass, + makeAdmin: makeAdmin + }; + + // validation + if (!tempPass) { + tempPass = stringutils.randomString(6); + } + + _err(pro_accounts.validateEmail(email)); + _err(pro_accounts.validateFullName(fullName)); + _err(pro_accounts.validatePassword(tempPass)); + + var existingAccount = pro_accounts.getAccountByEmail(email, null); + if (existingAccount) { + _err("There is already a account with that email address."); + } + + pro_accounts.createNewAccount(null, fullName, email, tempPass, makeAdmin); + var account = pro_accounts.getAccountByEmail(email, null); + + pro_accounts.setTempPassword(account, tempPass); + sendWelcomeEmail(account, tempPass); + + delete getSession().accountManagerFormData; + getSession().accountManagerMessage = "Account "+fullName+" ("+email+") created successfully."; + response.redirect('/ep/admin/account-manager/'); +} + +function sendWelcomeEmail(account, tempPass) { + var subj = "Welcome to EtherPad on "+pro_utils.getFullProDomain()+"!"; + var toAddr = account.email; + var fromAddr = pro_utils.getEmailFromAddr(); + + var body = renderTemplateAsString('pro/account/account-welcome-email.ejs', { + account: account, + adminAccount: getSessionProAccount(), + signinLink: pro_accounts.getTempSigninUrl(account, tempPass), + toEmail: toAddr, + siteName: pro_config.getConfig().siteName + }); + try { + sendEmail(toAddr, fromAddr, subj, {}, body); + } catch (ex) { + var d = DIV(); + d.push(P("Warning: unable to send welcome email.")); + if (pne_utils.isPNE()) { + d.push(P("Perhaps you have not ", + A({href: '/ep/admin/pne-config'}, "Configured SMTP on this server", "?"))); + } + getSession().accountManagerWarning = d; + } +} + +// Managing a single account. +function render_account_get(accountId) { + var account = pro_accounts.getAccountById(accountId); + if (!account) { + response.write("Account not found."); + return true; + } + pro_admin_control.renderAdminPage('manage-account', { + account: account, + errorDiv: _errorDiv, + warningDiv: _warningDiv + }); +} + +function render_account_post(accountId) { + if (request.params.cancel) { + response.redirect('/ep/admin/account-manager/'); + } + var newFullName = request.params.newFullName; + var newEmail = request.params.newEmail; + var newIsAdmin = !!request.params.newIsAdmin; + + _err(pro_accounts.validateEmail(newEmail)); + _err(pro_accounts.validateFullName(newFullName)); + + if ((!newIsAdmin) && (accountId == getSessionProAccount().id)) { + _err("You cannot remove your own administrator privileges."); + } + + var account = pro_accounts.getAccountById(accountId); + if (!account) { + response.write("Account not found."); + return true; + } + + pro_accounts.setEmail(account, newEmail); + pro_accounts.setFullName(account, newFullName); + pro_accounts.setIsAdmin(account, newIsAdmin); + + getSession().accountManageMessage = "Info updated."; + response.redirect('/ep/admin/account-manager/'); +} + +function render_delete_account_get(accountId) { + var account = pro_accounts.getAccountById(accountId); + if (!account) { + response.write("Account not found."); + return true; + } + pro_admin_control.renderAdminPage('delete-account', { + account: account, + errorDiv: _errorDiv + }); +} + +function render_delete_account_post(accountId) { + if (request.params.cancel) { + response.redirect("/ep/admin/account-manager/account/"+accountId); + } + + if (accountId == getSessionProAccount().id) { + getSession().accountManagerError = "You cannot delete your own account."; + response.redirect("/ep/admin/account-manager/account/"+accountId); + } + + var account = pro_accounts.getAccountById(accountId); + if (!account) { + response.write("Account not found."); + return true; + } + + pro_accounts.setDeleted(account); + getSession().accountManagerMessage = "The account "+account.fullName+" <"+account.email+"> has been deleted."; + response.redirect("/ep/admin/account-manager/"); +} + + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/license_manager_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/license_manager_control.js new file mode 100644 index 0000000..ca6d6a6 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/license_manager_control.js @@ -0,0 +1,128 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("fileutils.writeRealFile"); +import("stringutils"); + +import("etherpad.licensing"); +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); +import("etherpad.pne.pne_utils"); + +import("etherpad.control.pro.admin.pro_admin_control"); + +jimport("java.lang.System.out.println"); + +//---------------------------------------------------------------- +// license manager +//---------------------------------------------------------------- + +function getPath() { + return '/ep/admin/pne-license-manager/'; +} + +function _getTemplateData(data) { + var licenseInfo = licensing.getLicense(); + data.licenseInfo = licenseInfo; + data.isUnlicensed = !licenseInfo; + data.isEvaluation = licensing.isEvaluation(); + data.isExpired = licensing.isExpired(); + data.isTooOld = licensing.isVersionTooOld(); + data.errorMessage = (getSession().errorMessage || null); + data.runningVersionString = pne_utils.getVersionString(); + data.licenseVersionString = licensing.getVersionString(); + return data; +} + +function render_main_get() { + licensing.reloadLicense(); + var licenseInfo = licensing.getLicense(); + if (!licenseInfo || licensing.isExpired()) { + response.redirect(getPath()+'edit'); + } + + pro_admin_control.renderAdminPage('pne-license-manager', + _getTemplateData({edit: false})); +} + +function render_edit_get() { + licensing.reloadLicense(); + + if (request.params.btn) { response.redirect(request.path); } + + var licenseInfo = licensing.getLicense(); + var oldData = getSession().oldLicenseData; + if (!oldData) { + oldData = {}; + if (licenseInfo) { + oldData.orgName = licenseInfo.organizationName; + oldData.personName = licenseInfo.personName; + } + } + + pro_admin_control.renderAdminPage('pne-license-manager', + _getTemplateData({edit: true, oldData: oldData})); + + delete getSession().errorMessage; +} + +function render_edit_post() { + pne_utils.enableTrackingAgain(); + + function _trim(s) { + if (!s) { return ''; } + return stringutils.trim(s); + } + function _clean(s) { + s = s.replace(/\W/g, ''); + s = s.replace(/\+/g, ''); + return s; + } + + if (request.params.cancel) { + delete getSession().oldLicenseData; + response.redirect(getPath()); + } + + var personName = _trim(request.params.personName); + var orgName = _trim(request.params.orgName); + var licenseString = _clean(request.params.licenseString); + + getSession().oldLicenseData = { + personName: personName, orgName: orgName, licenseString: licenseString}; + + var key = [personName,orgName,licenseString].join(":"); + println("validating key [ "+key+" ]"); + + if (!licensing.isValidKey(key)) { + getSession().errorMessage = "Invalid License Key"; + response.redirect(request.path); + } + + // valid key. write to disk. + var writeSuccess = false; + try { + println("writing key file: ./data/license.key"); + writeRealFile("./data/license.key", key); + writeSuccess = true; + } catch (ex) { + println("exception: "+ex); + getSession().errorMessage = "Failed to write key to disk. (Do you have permission to write ./data/license.key ?)."; + } + response.redirect(getPath()); +} + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/pro_admin_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/pro_admin_control.js new file mode 100644 index 0000000..f9ce179 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/pro_admin_control.js @@ -0,0 +1,283 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("stringutils"); +import("funhtml.*"); +import("dispatch.{Dispatcher,DirMatcher,forward}"); + +import("etherpad.licensing"); +import("etherpad.control.admincontrol"); +import("etherpad.control.pro.admin.license_manager_control"); +import("etherpad.control.pro.admin.account_manager_control"); +import("etherpad.control.pro.admin.pro_config_control"); +import("etherpad.control.pro.admin.team_billing_control"); + +import("etherpad.pad.padutils"); + +import("etherpad.admin.shell"); +import("etherpad.sessions"); +import("etherpad.sessions.getSession"); + +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_accounts"); +import("etherpad.utils.*"); + +//---------------------------------------------------------------- + +var _pathPrefix = '/ep/admin/'; + +var _PRO = 1; +var _PNE_ONLY = 2; +var _ONDEMAND_ONLY = 3; + +function _getLeftnavItems() { + var nav = [ + _PRO, [ + [_PRO, null, "Admin"], + [_PNE_ONLY, "pne-dashboard", "Server Dashboard"], + [_PNE_ONLY, "pne-license-manager/", "Manage License"], + [_PRO, "account-manager/", "Manage Accounts"], + [_PRO, "recover-padtext", "Recover Pad Text"], + [_PRO, null, "Configuration"], + [_PRO, [[_PNE_ONLY, "pne-config", "Private Server Configuration"], + [_PRO, "pro-config", "Application Configuration"]]], + [_PNE_ONLY, null, "Documentation"], + [_PNE_ONLY, "/ep/pne-manual/", "Administrator's Manual"], + ] + ]; + return nav; +} + +function renderAdminLeftNav() { + function _make(x) { + if ((x[0] == _PNE_ONLY) && !pne_utils.isPNE()) { + return null; + } + if ((x[0] == _ONDEMAND_ONLY) && pne_utils.isPNE()) { + return null; + } + + if (x[1] instanceof Array) { + return _makelist(x[1]); + } else { + return _makeitem(x); + } + } + var selected; + function _makeitem(x) { + if (x[1]) { + var p = x[1]; + if (x[1].charAt(0) != '/') { + p = _pathPrefix+p; + } + var li = LI(A({href: p}, x[2])); + if (stringutils.startsWith(request.path, p)) { + // select the longest prefix match. + if (! selected || p.length > selected.path.length) { + selected = {path: p, li: li}; + } + } + return li; + } else { + return LI(DIV({className: 'leftnav-title'}, x[2])); + } + } + function _makelist(x) { + var ul = UL(); + x.forEach(function(y) { + var t = _make(y); + if (t) { ul.push(t); } + }); + return ul; + } + var d = DIV(_make(_getLeftnavItems())); + if (selected) { + selected.li.attribs.className = "selected"; + } + // leftnav looks stupid when it's not very tall. + for (var i = 0; i < 10; i++) { d.push(BR()); } + return d; +} + +function renderAdminPage(p, data) { + appjet.requestCache.proTopNavSelection = 'admin'; + function getAdminContent() { + if (typeof(p) == 'function') { + return p(); + } else { + return renderTemplateAsString('pro/admin/'+p+'.ejs', data); + } + } + renderFramed('pro/admin/admin-template.ejs', { + getAdminContent: getAdminContent, + renderAdminLeftNav: renderAdminLeftNav, + validLicense: pne_utils.isServerLicensed(), + }); +} + +//---------------------------------------------------------------- + +function onRequest() { + var disp = new Dispatcher(); + disp.addLocations([ + [DirMatcher(license_manager_control.getPath()), forward(license_manager_control)], + [DirMatcher('/ep/admin/account-manager/'), forward(account_manager_control)], + [DirMatcher('/ep/admin/pro-config/'), forward(pro_config_control)], + [DirMatcher('/ep/admin/billing/'), forward(team_billing_control)], + ]); + + if (disp.dispatch()) { + return true; + } + + // request will be handled by this module. + pro_accounts.requireAdminAccount(); +} + +function render_main() { +// renderAdminPage('admin'); + response.redirect('/ep/admin/account-manager/') +} + +function render_pne_dashboard() { + renderAdminPage('pne-dashboard', { + renderUptime: admincontrol.renderServerUptime, + renderResponseCodes: admincontrol.renderResponseCodes, + renderPadConnections: admincontrol.renderPadConnections, + renderTransportStats: admincontrol.renderCometStats, + todayActiveUsers: licensing.getActiveUserCount(), + userQuota: licensing.getActiveUserQuota() + }); +} + +var _documentedServerOptions = [ + 'listen', + 'listenSecure', + 'transportUseWildcardSubdomains', + 'sslKeyStore', + 'sslKeyPassword', + 'etherpad.soffice', + 'etherpad.adminPass', + 'etherpad.SQL_JDBC_DRIVER', + 'etherpad.SQL_JDBC_URL', + 'etherpad.SQL_USERNAME', + 'etherpad.SQL_PASSWORD', + 'smtpServer', + 'smtpUser', + 'smtpPass', + 'configFile', + 'etherpad.licenseKey', + 'verbose' +]; + +function render_pne_config_get() { + renderAdminPage('pne-config', { + propKeys: _documentedServerOptions, + appjetConfig: appjet.config + }); +} + +function render_pne_advanced_get() { + response.redirect("/ep/admin/shell"); +} + +function render_shell_get() { + if (!(pne_utils.isPNE() || sessions.isAnEtherpadAdmin())) { + return false; + } + appjet.requestCache.proTopNavSelection = 'admin'; + renderAdminPage('pne-shell', { + oldCmd: getSession().pneAdminShellCmd, + result: getSession().pneAdminShellResult, + elapsedMs: getSession().pneAdminShellElapsed + }); + delete getSession().pneAdminShellResult; + delete getSession().pneAdminShellElapsed; +} + +function render_shell_post() { + if (!(pne_utils.isPNE() || sessions.isAnEtherpadAdmin())) { + return false; + } + var cmd = request.params.cmd; + var start = +(new Date); + getSession().pneAdminShellCmd = cmd; + getSession().pneAdminShellResult = shell.getResult(cmd); + getSession().pneAdminShellElapsed = +(new Date) - start; + response.redirect(request.path); +} + +function render_recover_padtext_get() { + function getNumRevisions(localPadId) { + return padutils.accessPadLocal(localPadId, function(pad) { + if (!pad.exists()) { return null; } + return 1+pad.getHeadRevisionNumber(); + }); + } + function getPadText(localPadId, revNum) { + return padutils.accessPadLocal(localPadId, function(pad) { + if (!pad.exists()) { return null; } + return pad.getRevisionText(revNum); + }); + } + + var localPadId = request.params.localPadId; + var revNum = request.params.revNum; + + var d = DIV({style: "font-size: .8em;"}); + + d.push(FORM({action: request.path, method: "get"}, + P({style: "margin-top: 0;"}, LABEL("Pad ID: "), + INPUT({type: "text", name: "localPadId", value: localPadId || ""}), + INPUT({type: "submit", value: "Submit"})))); + + var showPadHelp = false; + var revisions = null; + + if (!localPadId) { + showPadHelp = true; + } else { + revisions = getNumRevisions(localPadId); + if (!revisions) { + d.push(P("Pad not found: "+localPadId)); + } else { + d.push(P(B(localPadId), " has ", revisions, " revisions.")); + d.push(P("Enter a revision number (0-"+revisions+") to recover the pad text for that revision:")); + d.push(FORM({action: request.path, method: "get"}, + P(LABEL("Revision number:"), + INPUT({type: "hidden", name: "localPadId", value: localPadId}), + INPUT({type: "text", name: "revNum", value: revNum || (revisions - 1)}), + INPUT({type: "submit", value: "Submit"})))); + } + } + + if (showPadHelp) { + d.push(P({style: "font-size: 1em; color: #555;"}, + 'The pad ID is the same as the URL to the pad, without the leading "/".', + ' For example, if the pad lives at http://pad.spline.inf.fu-berlin.de/foobar,', + ' then the pad ID is "foobar" (without the quotes).')) + } + + if (revisions && revNum && (revNum < revisions)) { + var padText = getPadText(localPadId, revNum); + d.push(P(B("Pad text for ["+localPadId+"] revision #"+revNum))); + d.push(DIV({style: "font-family: monospace; border: 1px solid #ccc; background: #ffe; padding: 1em;"}, padText)); + } + + renderAdminPage(function() { return d; }); +} + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/pro_config_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/pro_config_control.js new file mode 100644 index 0000000..b03da45 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/pro_config_control.js @@ -0,0 +1,54 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("funhtml.*"); + +import("etherpad.sessions.getSession"); +import("etherpad.control.pro.admin.pro_admin_control"); +import("etherpad.pro.pro_config"); + +function _renderTopDiv(mid, htmlId) { + var m = getSession()[mid]; + if (m) { + delete getSession()[mid]; + return DIV({id: htmlId}, m); + } else { + return ''; + } +} + +function _messageDiv() { + return _renderTopDiv('proConfigMessage', 'pro-config-message'); +} + +function render_main_get() { + pro_config.reloadConfig(); + var config = pro_config.getConfig(); + pro_admin_control.renderAdminPage('pro-config', { + config: config, + messageDiv: _messageDiv + }); +} + +function render_main_post() { + pro_config.setConfigVal('siteName', request.params.siteName); + pro_config.setConfigVal('alwaysHttps', !!request.params.alwaysHttps); + pro_config.setConfigVal('defaultPadText', request.params.defaultPadText); + getSession().proConfigMessage = "New settings applied."; + response.redirect(request.path); +} + + diff --git a/trunk/etherpad/src/etherpad/control/pro/admin/team_billing_control.js b/trunk/etherpad/src/etherpad/control/pro/admin/team_billing_control.js new file mode 100644 index 0000000..5be6a0e --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/admin/team_billing_control.js @@ -0,0 +1,447 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("dateutils"); +import("email.sendEmail"); +import("fastJSON"); +import("funhtml.*"); +import("jsutils.*"); +import("sqlbase.sqlcommon.inTransaction"); +import("stringutils.*"); + +import("etherpad.billing.billing"); +import("etherpad.billing.fields"); +import("etherpad.billing.team_billing"); +import("etherpad.control.pro.admin.pro_admin_control"); +import("etherpad.globals"); +import("etherpad.helpers"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_utils"); +import("etherpad.sessions"); +import("etherpad.store.checkout"); +import("etherpad.utils.*"); + +import("static.js.billing_shared.{billing=>billingJS}"); + +var billingButtonName = "Confirm" + +function _cart() { + var s = sessions.getSession(); + if (! s.proBillingCart) { + s.proBillingCart = {}; + } + return s.proBillingCart; +} + +function _billingForm() { + return renderTemplateAsString('store/eepnet-checkout/billing-info.ejs', { + cart: _cart(), + billingButtonName: billingButtonName, + billingFinalPhrase: "", + helpers: helpers, + errorIfInvalid: _errorIfInvalid, + billing: billingJS, + obfuscateCC: checkout.obfuscateCC, + dollars: checkout.dollars, + countryList: fields.countryList, + usaStateList: fields.usaStateList, + getFullSuperdomainHost: pro_utils.getFullSuperdomainHost, + showCouponCode: true, + }); +} + +function _plural(num) { + return (num == 1 ? "" : "s"); +} + +function _billingSummary(domainId, subscription) { + var paymentInfo = team_billing.getRecurringBillingInfo(domainId); + if (! paymentInfo) { + return; + } + var latestInvoice = team_billing.getLatestPaidInvoice(subscription.id); + var usersSoFar = team_billing.getMaxUsers(domainId); + var costSoFar = team_billing.calculateSubscriptionCost(usersSoFar, subscription.coupon); + + var lastPaymentString = + (latestInvoice ? + "US $"+checkout.dollars(billing.centsToDollars(latestInvoice.amt))+ + " ("+latestInvoice.users+" account"+_plural(latestInvoice.users)+")"+ + ", on "+checkout.formatDate(latestInvoice.time) : + "None"); + + var coupon = false; + if (subscription.coupon) { + println("has a coupon: "+subscription.coupon); + var cval = team_billing.getCouponValue(subscription.coupon); + coupon = []; + if (cval.freeUsers) { + coupon.push(cval.freeUsers+" free user"+(cval.freeUsers == 1 ? "" : "s")); + } + if (cval.pctDiscount) { + coupon.push(cval.pctDiscount+"% savings"); + } + coupon = coupon.join(", "); + } + + return { + fullName: paymentInfo.fullname, + paymentSummary: + paymentInfo.paymentsummary + + (paymentInfo.expiration ? + ", expires "+checkout.formatExpiration(paymentInfo.expiration) : + ""), + lastPayment: lastPaymentString, + nextPayment: checkout.formatDate(subscription.paidThrough), + maxUsers: usersSoFar, + estimatedPayment: "US $"+checkout.dollars(costSoFar), + coupon: coupon + } +} + +function _statusMessage() { + if (_cart().statusMessage) { + return toHTML(P({style: "color: green;"}, _cart().statusMessage)); + } else { + return ''; + } +} + +function renderMainPage(doEdit) { + var cart = _cart(); + var domainId = domains.getRequestDomainId(); + var subscription = team_billing.getSubscriptionForCustomer(domainId); + var pendingInvoice = team_billing.getLatestPendingInvoice(domainId) + var usersSoFar = team_billing.getMaxUsers(domainId); + var costSoFar = team_billing.calculateSubscriptionCost(usersSoFar, subscription && subscription.coupon); + + checkout.guessBillingNames(cart, pro_accounts.getSessionProAccount().fullName); + if (! cart.billingReferralCode) { + if (subscription && subscription.coupon) { + cart.billingReferralCode = subscription.coupon; + } + } + + var summary = _billingSummary(domainId, subscription); + if (! summary) { + doEdit = true; + } + + pro_admin_control.renderAdminPage('manage-billing', { + billingForm: _billingForm, + doEdit: doEdit, + paymentInfo: summary, + getFullSuperdomainHost: pro_utils.getFullSuperdomainHost, + firstCharge: checkout.formatDate(subscription ? subscription.paidThrough : dateutils.nextMonth(new Date)), + billingButtonName: billingButtonName, + errorDiv: _errorDiv, + showBackButton: (summary != undefined), + statusMessage: _statusMessage, + isBehind: (subscription ? subscription.paidThrough < Date.now() - 86400*1000 : false), + amountDue: "US $"+checkout.dollars(billing.centsToDollars(pendingInvoice ? pendingInvoice.amt : costSoFar*100)), + cart: _cart() + }); + + delete _cart().errorId; + delete _cart().errorMsg; + delete _cart().statusMessage; +} + +function render_main() { + renderMainPage(false); +} + +function render_edit() { + renderMainPage(true); +} + +function _errorDiv() { + var m = _cart().errorMsg; + if (m) { + return DIV({className: 'errormsg', id: 'errormsg'}, m); + } else { + return ''; + } +} + +function _validationError(id, errorMessage) { + var cart = _cart(); + cart.errorMsg = errorMessage; + cart.errorId = {}; + if (id instanceof Array) { + id.forEach(function(k) { + cart.errorId[k] = true; + }); + } else { + cart.errorId[id] = true; + } + response.redirect('/ep/admin/billing/edit'); +} + +function _errorIfInvalid(id) { + var cart = _cart(); + if (cart.errorId && cart.errorId[id]) { + return 'error'; + } else { + return ''; + } +} + +function paypalNotifyUrl() { + return request.scheme+"://"+pro_utils.getFullSuperdomainHost()+"/ep/store/paypalnotify"; +} + +function _paymentSummary(payInfo) { + return payInfo.cardType + " ending in " + payInfo.cardNumber.substr(-4); +} + +function _expiration(payInfo) { + return payInfo.cardExpiration; +} + +function _attemptAuthorization(success_f) { + var cart = _cart(); + var domain = domains.getRequestDomainRecord(); + var domainId = domain.id; + var domainName = domain.subDomain; + var payInfo = checkout.generatePayInfo(cart); + var proAccount = pro_accounts.getSessionProAccount(); + var fullName = cart.billingFirstName+" "+cart.billingLastName; + var email = proAccount.email; + + // PCI rules require that we not store the CVV longer than necessary to complete the transaction + var savedCvv = payInfo.cardCvv; + delete payInfo.cardCvv; + checkout.writeToEncryptedLog(fastJSON.stringify({date: String(new Date()), domain: domain, payInfo: payInfo})); + payInfo.cardCvv = savedCvv; + + var result = billing.authorizePurchase(payInfo, paypalNotifyUrl()); + if (result.status == 'success') { + billing.log({type: 'new-subscription', + name: fullName, + domainId: domainId, + domainName: domainName}); + success_f(result); + } else if (result.status == 'pending') { + _validationError('', "Your authorization is pending. When it clears, your account will be activated. "+ + "You may choose to pay by different means now, or wait until your authorization clears."); + } else if (result.status == 'failure') { + var paypalResult = result.debug; + billing.log({'type': 'FATAL', value: "Direct purchase failed on paypal.", cart: cart, paypal: paypalResult}); + checkout.validateErrorFields(_validationError, "There seems to be an error in your billing information."+ + " Please verify and correct your ", + result.errorField.userErrors); + checkout.validateErrorFields(_validationError, "The bank declined your billing information. Please try a different ", + result.errorField.permanentErrors); + _validationError('', "A temporary error has prevented processing of your payment. Please try again later."); + } else { + billing.log({'type': 'FATAL', value: "Unknown error: "+result.status+" - debug: "+result.debug}); + sendEmail('support@pad.spline.inf.fu-berlin.de', 'urgent@pad.spline.inf.fu-berlin.de', 'UNKNOWN ERROR WARNING!', {}, + "Hey,\n\nThis is a billing system error. Some unknown error occurred. "+ + "This shouldn't ever happen. Probably good to let J.D. know. <grin>\n\n"+ + fastJSON.stringify(cart)); + _validationError('', "An unknown error occurred. We're looking into it!") + } +} + +function _processNewSubscription() { + _attemptAuthorization(function(result) { + var domain = domains.getRequestDomainRecord(); + var domainId = domain.id; + var domainName = domain.subDomain; + + var cart = _cart(); + var payInfo = checkout.generatePayInfo(cart); + var proAccount = pro_accounts.getSessionProAccount(); + var fullName = cart.billingFirstName+" "+cart.billingLastName; + var email = proAccount.email; + + inTransaction(function() { + + var subscriptionId = team_billing.createSubscription(domainId, cart.billingReferralCode); + + team_billing.setRecurringBillingInfo( + domainId, + fullName, + email, + _paymentSummary(payInfo), + _expiration(payInfo), + result.purchaseInfo.paypalId); + }); + + if (globals.isProduction()) { + sendEmail('sales@pad.spline.inf.fu-berlin.de', 'sales@pad.spline.inf.fu-berlin.de', "EtherPad: New paid pro account for "+fullName, {}, + "This is an automatic notification.\n\n"+fullName+" ("+email+") successfully set up "+ + "a billing profile for domain: "+domainName+"."); + } + }); +} + +function _updateExistingSubscription(subscription) { + var cart = _cart(); + + _attemptAuthorization(function(result) { + inTransaction(function() { + var cart = _cart(); + var domain = domains.getRequestDomainId(); + var payInfo = checkout.generatePayInfo(cart); + var proAccount = pro_accounts.getSessionProAccount(); + var fullName = cart.billingFirstName+" "+cart.billingLastName; + var email = proAccount.email; + + var subscriptionId = subscription.id; + + team_billing.setRecurringBillingInfo( + domain, + fullName, + email, + _paymentSummary(payInfo), + _expiration(payInfo), + result.purchaseInfo.paypalId); + }); + }); + + if (subscription.paidThrough < new Date) { + // if they're behind, do the purchase! + if (team_billing.processSubscription(subscription)) { + cart.statusMessage = "Your payment was successful, and your account is now up to date! You will receive a receipt by email." + } else { + cart.statusMessage = "Your payment failed; you will receive further instructions by email."; + } + } +} + +function _processBillingInfo() { + var cart = _cart(); + var domain = domains.getRequestDomainId(); + + var subscription = team_billing.getSubscriptionForCustomer(domain); + if (! subscription) { + _processNewSubscription(); + response.redirect('/ep/admin/billing/'); + } else { + team_billing.updateSubscriptionCouponCode(subscription.id, cart.billingReferralCode); + if (cart.billingCCNumber.length > 0) { + _updateExistingSubscription(subscription); + } + response.redirect('/ep/admin/billing') + } +} + +function _processPaypalPurchase() { + var domain = domains.getRequestDomainId(); + billing.log({type: "paypal-attempt", + domain: domain, + message: "Someone tried to use paypal to pay for on-demand."+ + " They got an error message. If this happens a lot, we should implement paypal."}) + java.lang.Thread.sleep(5000); + _validationError('billingPurchaseType', "There was an error contacting PayPal. Please try another payment type.") +} + +function _processInvoicePurchase() { + var output = [ + "Name: "+cart.billingFirstName+" "+cart.billingLastName, + "\nAddress: ", + cart.billingAddressLine1+(cart.billingAddressLine2.length > 0 ? "\n"+cart.billingAddressLine2 : ""), + cart.billingCity + ", " + (cart.billingState.length > 0 ? cart.billingState : cart.billingProvince), + cart.billingZipCode.length > 0 ? cart.billingZipCode : cart.billingPostalCode, + cart.billingCountry, + "\nEmail: ", + pro_accounts.getSessionProAccount().email + ].join("\n"); + var recipient = (globals.isProduction() ? 'sales@pad.spline.inf.fu-berlin.de' : 'jd@appjet.com'); + sendEmail( + recipient, + 'sales@pad.spline.inf.fu-berlin.de', + 'Invoice payment request - '+pro_utils.getProRequestSubdomain(), + {}, + "Hi there,\n\nA pro user tried to pay by invoice. Their information follows."+ + "\n\nThanks!\n\n"+output); + _validationError('', "Your information has been sent to our sales department; a salesperson will contact you shortly regarding your invoice request.") +} + +function render_apply() { + var cart = _cart(); + eachProperty(request.params, function(k, v) { + if (startsWith(k, "billing")) { + if (k == "billingCCNumber" && v.charAt(0) == 'X') { return; } + cart[k] = toHTML(v); + } + }); + + if (! request.params.backbutton) { + var allPaymentFields = ["billingCCNumber", "billingExpirationMonth", "billingExpirationYear", "billingCSC", "billingAddressLine1", "billingAddressLine2", "billingCity", "billingState", "billingZipCode", "billingProvince", "billingPostalCode"]; + var allBlank = true; + allPaymentFields.forEach(function(field) { if (cart[field].length > 0) { allBlank = false; }}); + if (! allBlank) { + checkout.validateBillingCart(_validationError, cart); + } + } else { + response.redirect("/ep/admin/billing/"); + } + + var couponCode = cart.billingReferralCode; + + if (couponCode.length != 0 && (couponCode.length != 8 || ! team_billing.getCouponValue(couponCode))) { + _validationError('billingReferralCode', 'Invalid referral code entered. Please verify your code and try again.'); + } + + if (cart.billingPurchaseType == 'paypal') { + _processPaypalPurchase(); + } else if (cart.billingPurchaseType == 'invoice') { + _processInvoicePurchase(); + } + + _processBillingInfo(); +} + +function handlePaypalNotify() { + // XXX: handle delayed paypal authorization +} + +function render_invoices() { + if (request.params.id) { + var purchaseId = team_billing.getSubscriptionForCustomer(domains.getRequestDomainId()).id; + var invoice = billing.getInvoice(request.params.id); + if (invoice.purchase != purchaseId) { + response.redirect(request.path); + } + + var transaction; + var adjustments = billing.getAdjustments(invoice.id); + if (adjustments.length == 1) { + transaction = billing.getTransaction(adjustments[0].transaction); + } + + pro_admin_control.renderAdminPage('single-invoice', { + formatDate: checkout.formatDate, + dollars: checkout.dollars, + centsToDollars: billing.centsToDollars, + invoice: invoice, + transaction: transaction + }); + } else { + var invoices = team_billing.getAllInvoices(domains.getRequestDomainId()); + + pro_admin_control.renderAdminPage('billing-invoices', { + invoices: invoices, + formatDate: checkout.formatDate, + dollars: checkout.dollars, + centsToDollars: billing.centsToDollars + }); + } +}
\ No newline at end of file diff --git a/trunk/etherpad/src/etherpad/control/pro/pro_main_control.js b/trunk/etherpad/src/etherpad/control/pro/pro_main_control.js new file mode 100644 index 0000000..b4e3bc4 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/pro_main_control.js @@ -0,0 +1,150 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("stringutils"); +import("dispatch.{Dispatcher,DirMatcher,forward}"); +import("funhtml.*"); +import("cache_utils.syncedWithCache"); + +import("etherpad.helpers"); +import("etherpad.utils.*"); +import("etherpad.sessions.getSession"); +import("etherpad.licensing"); +import("etherpad.pne.pne_utils"); +import("etherpad.pro.pro_pad_db"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.pro.pro_padlist"); + +import("etherpad.control.pro.account_control"); +import("etherpad.control.pro.pro_padlist_control"); +import("etherpad.control.pro.admin.pro_admin_control"); +import("etherpad.control.pro.admin.account_manager_control"); + +import("etherpad.pad.activepads"); +import("etherpad.pad.model"); + + +function onRequest() { + var disp = new Dispatcher(); + disp.addLocations([ + [DirMatcher('/ep/account/'), forward(account_control)], + [DirMatcher('/ep/admin/'), forward(pro_admin_control)], + [DirMatcher('/ep/padlist/'), forward(pro_padlist_control)], + ]); + return disp.dispatch(); +} + +function render_main() { + if (request.path == '/ep/') { + response.redirect('/'); + } + + // recent pad list + var livePads = pro_pad_db.listLiveDomainPads(); + var recentPads = pro_pad_db.listAllDomainPads(); + + var renderLivePads = function() { + return pro_padlist.renderPadList(livePads, ['title', 'connectedUsers'], 10); + } + + var renderRecentPads = function() { + return pro_padlist.renderPadList(recentPads, ['title'], 10); + }; + + var r = domains.getRequestDomainRecord(); + + renderFramed('pro/pro_home.ejs', { + isEvaluation: licensing.isEvaluation(), + account: getSessionProAccount(), + isPNE: pne_utils.isPNE(), + pneVersion: pne_utils.getVersionString(), + livePads: livePads, + recentPads: recentPads, + renderRecentPads: renderRecentPads, + renderLivePads: renderLivePads, + orgName: r.orgName + }); + return true; +} + +function render_finish_activation_get() { + if (!isActivationAllowed()) { + response.redirect('/'); + } + + var accountList = pro_accounts.listAllDomainAccounts(); + if (accountList.length > 1) { + response.redirect('/'); + } + if (accountList.length == 0) { + throw Error("accountList.length should never be 0."); + } + + var acct = accountList[0]; + var tempPass = stringutils.randomString(10); + pro_accounts.setTempPassword(acct, tempPass); + account_manager_control.sendWelcomeEmail(acct, tempPass); + + var domainId = domains.getRequestDomainId(); + + syncedWithCache('pro-activations', function(c) { + delete c[domainId]; + }); + + renderNoticeString( + DIV({style: "font-size: 16pt; border: 1px solid green; background: #eeffee; margin: 2em 4em; padding: 1em;"}, + P("Success! You will receive an email shortly with instructions."), + DIV({style: "display: none;", id: "reference"}, acct.id, ":", tempPass))); +} + +function isActivationAllowed() { + if (request.path != '/ep/finish-activation') { + return false; + } + var allowed = false; + var domainId = domains.getRequestDomainId(); + return syncedWithCache('pro-activations', function(c) { + if (c[domainId]) { + return true; + } + return false; + }); +} + +function render_payment_required_get() { + // Users get to this page when there is a problem with billing: + // possibilities: + // * they try to create a new account but they have not entered + // payment information + // + // * their credit card lapses and any pro request fails. + // + // * others? + + var message = getSession().billingProblem || "A payment is required to proceed."; + var adminList = pro_accounts.listAllDomainAdmins(); + + renderFramed("pro/pro-payment-required.ejs", { + message: message, + isAdmin: pro_accounts.isAdminSignedIn(), + adminList: adminList + }); +} + + + diff --git a/trunk/etherpad/src/etherpad/control/pro/pro_padlist_control.js b/trunk/etherpad/src/etherpad/control/pro/pro_padlist_control.js new file mode 100644 index 0000000..9a90c67 --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/pro/pro_padlist_control.js @@ -0,0 +1,200 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("funhtml.*"); +import("jsutils.*"); +import("stringutils"); + +import("etherpad.sessions.getSession"); +import("etherpad.utils.*"); +import("etherpad.helpers"); +import("etherpad.pad.exporthtml"); +import("etherpad.pad.padutils"); +import("etherpad.pro.pro_padmeta"); +import("etherpad.pro.pro_pad_db"); +import("etherpad.pro.domains"); +import("etherpad.pro.pro_accounts"); +import("etherpad.pro.pro_padlist"); + +jimport("java.lang.System.out.println"); + +function onRequest(name) { + if (name == "all_pads.zip") { + render_all_pads_zip_get(); + return true; + } else { + return false; + } +} + +function _getBaseUrl() { return "/ep/padlist/"; } + +function _renderPadNav() { + var d = DIV({id: "padlist-nav"}); + var ul = UL(); + var items = [ + ['allpads', 'all-pads', "All Pads"], + ['mypads', 'my-pads', "My Pads"], + ['archivedpads', 'archived-pads', "Archived Pads"] + ]; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + var cn = ""; + if (request.path.split("/").slice(-1)[0] == item[1]) { + cn = "selected"; + } + ul.push(LI(A({id: "nav-"+item[1], href: _getBaseUrl()+item[1], className: cn}, item[2]))); + } + ul.push(html(helpers.clearFloats())); + d.push(ul); + d.push(FORM({id: "newpadform", method: "get", action: "/ep/pad/newpad"}, + INPUT({type: "submit", value: "New Pad"}))); + d.push(html(helpers.clearFloats())); + return d; +} + +function _renderPage(name, data) { + getSession().latestPadlistView = request.path + "?" + request.query; + var r = domains.getRequestDomainRecord(); + appjet.requestCache.proTopNavSelection = 'padlist'; + data.renderPadNav = _renderPadNav; + data.orgName = r.orgName; + data.renderNotice = function() { + var m = getSession().padlistMessage; + if (m) { + delete getSession().padlistMessage; + return DIV({className: "padlist-notice"}, m); + } else { + return ""; + } + }; + + renderFramed("pro/padlist/"+name+".ejs", data); +} + +function _renderListPage(padList, showingDesc, columns) { + _renderPage("pro-padlist", { + padList: padList, + renderPadList: function() { + return pro_padlist.renderPadList(padList, columns); + }, + renderShowingDesc: function(count) { + return DIV({id: "showing-desc"}, + "Showing "+showingDesc+" ("+count+")."); + }, + isAdmin: pro_accounts.isAdminSignedIn() + }); +} + +function render_main() { + if (!getSession().latestPadlistView) { + getSession().latestPadlistView = "/ep/padlist/all-pads"; + } + response.redirect(getSession().latestPadlistView); +} + +function render_all_pads_get() { + _renderListPage( + pro_pad_db.listAllDomainPads(), + "all pads", + ['secure', 'title', 'lastEditedDate', 'editors', 'actions']); +} + +function render_all_pads_zip_get() { + if (! pro_accounts.isAdminSignedIn()) { + response.redirect(_getBaseUrl()+"all-pads"); + } + var bytes = new java.io.ByteArrayOutputStream(); + var zos = new java.util.zip.ZipOutputStream(bytes); + + var pads = pro_pad_db.listAllDomainPads(); + pads.forEach(function(pad) { + var padHtml; + var title; + padutils.accessPadLocal(pad.localPadId, function(p) { + title = padutils.getProDisplayTitle(pad.localPadId, pad.title); + padHtml = exporthtml.getPadHTML(p); + }, "r"); + + title = title.replace(/[^\w\s]/g, "-") + ".html"; + zos.putNextEntry(new java.util.zip.ZipEntry(title)); + var padBytes = (new java.lang.String(renderTemplateAsString('pad/exporthtml.ejs', { + content: padHtml, + pre: false + }))).getBytes("UTF-8"); + + zos.write(padBytes, 0, padBytes.length); + zos.closeEntry(); + }); + zos.close(); + response.setContentType("application/zip"); + response.writeBytes(bytes.toByteArray()); +} + +function render_my_pads_get() { + _renderListPage( + pro_pad_db.listMyPads(), + "pads created by me", + ['secure', 'title', 'lastEditedDate', 'editors', 'actions']); +} + +function render_archived_pads_get() { + helpers.addClientVars({ + showingArchivedPads: true + }); + _renderListPage( + pro_pad_db.listArchivedPads(), + "archived pads", + ['secure', 'title', 'lastEditedDate', 'actions']); +} + +function render_edited_by_get() { + var editorId = request.params.editorId; + var editorName = pro_accounts.getFullNameById(editorId); + _renderListPage( + pro_pad_db.listPadsByEditor(editorId), + "pads edited by "+editorName, + ['secure', 'title', 'lastEditedDate', 'editors', 'actions']); +} + +function render_delete_post() { + var localPadId = request.params.padIdToDelete; + + pro_padmeta.accessProPadLocal(localPadId, function(propad) { + propad.markDeleted(); + getSession().padlistMessage = 'Pad "'+propad.getDisplayTitle()+'" has been deleted.'; + }); + + response.redirect(request.params.returnPath); +} + +function render_toggle_archive_post() { + var localPadId = request.params.padIdToToggleArchive; + + pro_padmeta.accessProPadLocal(localPadId, function(propad) { + if (propad.isArchived()) { + propad.unmarkArchived(); + getSession().padlistMessage = 'Pad "'+propad.getDisplayTitle()+'" has been un-archived.'; + } else { + propad.markArchived(); + getSession().padlistMessage = 'Pad "'+propad.getDisplayTitle()+'" has been archived. You can view archived pads by clicking on the "Archived" tab at the top of the pad list.'; + } + }); + + response.redirect(request.params.returnPath); +} + + |