Files
cloudron-box/src/js/index.js

765 lines
28 KiB
JavaScript
Raw Normal View History

2018-01-22 13:01:38 -08:00
'use strict';
/* global angular:false */
/* global showdown:false */
/* global moment:false */
/* global $:false */
2018-01-22 13:01:38 -08:00
// deal with accessToken in the query, this is passed for example on password reset and account setup upon invite
var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {});
if (search.accessToken) {
localStorage.token = search.accessToken;
// strip the accessToken and expiresAt, then preserve the rest
delete search.accessToken;
delete search.expiresAt;
// this will reload the page as this is not a hash change
window.location.search = encodeURIComponent(Object.keys(search).map(function (key) { return key + '=' + search[key]; }).join('&'));
}
// poor man's async in the global namespace
function asyncForEachParallel(items, handler, callback) {
var alreadyDone = 0;
var errored = false;
if (items.length === 0) return callback();
function done(error) {
// do nothing if already called back due to error
if (errored) return;
if (error) {
errored = true;
return callback(error);
}
++alreadyDone;
// we are done
if (alreadyDone === items.length) callback();
}
for (var i = 0; i < items.length; ++i) {
handler(items[i], done);
}
}
function asyncForEach(items, handler, callback) {
var cur = 0;
if (items.length === 0) return callback();
(function iterator() {
handler(items[cur], function (error) {
if (error) return callback(error);
if (cur >= items.length-1) return callback();
++cur;
iterator();
});
})();
}
2018-01-22 13:01:38 -08:00
function asyncSeries(funcs, callback) {
var cur = 0;
if (funcs.length === 0) return callback();
(function iterator() {
funcs[cur](function (error) {
if (error) return callback(error);
if (cur >= funcs.length-1) return callback();
++cur;
iterator();
});
})();
}
2018-01-22 13:01:38 -08:00
// create main application module
var app = angular.module('Application', ['ngFitText', 'ngRoute', 'ngAnimate', 'ngSanitize', 'angular-md5', 'base64', 'slick', 'ui-notification', 'ui.bootstrap', 'ui.bootstrap-slider', 'ngTld', 'ui.multiselect']);
app.config(['NotificationProvider', function (NotificationProvider) {
NotificationProvider.setOptions({
delay: 5000,
startTop: 60,
positionX: 'left',
templateUrl: 'notification.html'
});
}]);
// setup all major application routes
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', {
redirectTo: '/apps'
}).when('/users', {
controller: 'UsersController',
templateUrl: 'views/users.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/appstore', {
controller: 'AppStoreController',
templateUrl: 'views/appstore.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/appstore/:appId', {
controller: 'AppStoreController',
templateUrl: 'views/appstore.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/apps', {
controller: 'AppsController',
templateUrl: 'views/apps.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/account', {
controller: 'AccountController',
templateUrl: 'views/account.html?<%= revision %>'
2018-06-07 14:22:48 +02:00
}).when('/backups', {
controller: 'BackupsController',
templateUrl: 'views/backups.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/graphs', {
controller: 'GraphsController',
templateUrl: 'views/graphs.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/domains', {
controller: 'DomainsController',
templateUrl: 'views/domains.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/email', {
controller: 'EmailController',
templateUrl: 'views/email.html?<%= revision %>'
}).when('/email/:domain', {
controller: 'EmailController',
templateUrl: 'views/email.html?<%= revision %>'
}).when('/notifications', {
controller: 'NotificationsController',
templateUrl: 'views/notifications.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/settings', {
controller: 'SettingsController',
templateUrl: 'views/settings.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/activity', {
controller: 'ActivityController',
templateUrl: 'views/activity.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/support', {
controller: 'SupportController',
templateUrl: 'views/support.html?<%= revision %>'
}).when('/system', {
controller: 'SystemController',
templateUrl: 'views/system.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).when('/tokens', {
controller: 'TokensController',
templateUrl: 'views/tokens.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
}).otherwise({ redirectTo: '/'});
}]);
// keep in sync with appdb.js
var ISTATES = {
PENDING_INSTALL: 'pending_install',
PENDING_CLONE: 'pending_clone',
PENDING_CONFIGURE: 'pending_configure',
PENDING_UNINSTALL: 'pending_uninstall',
PENDING_RESTORE: 'pending_restore',
PENDING_UPDATE: 'pending_update',
PENDING_FORCE_UPDATE: 'pending_force_update',
PENDING_BACKUP: 'pending_backup',
ERROR: 'error',
INSTALLED: 'installed'
};
var HSTATES = {
HEALTHY: 'healthy',
UNHEALTHY: 'unhealthy',
ERROR: 'error',
DEAD: 'dead'
};
app.filter('installError', function () {
return function (app) {
if (app.installationState === ISTATES.ERROR) return true;
if (app.installationState === ISTATES.INSTALLED) {
// app.health can also be null to indicate insufficient data
if (app.health === HSTATES.UNHEALTHY || app.health === HSTATES.ERROR || app.health === HSTATES.DEAD) return true;
}
return false;
};
});
app.filter('installSuccess', function () {
return function (app) {
return app.installationState === ISTATES.INSTALLED;
};
});
app.filter('appIsInstalledAndHealthy', function () {
return function (app) {
return (app.installationState === ISTATES.INSTALLED && app.health === HSTATES.HEALTHY);
}
});
2018-01-22 13:01:38 -08:00
app.filter('activeOAuthClients', function () {
return function (clients, user) {
2018-08-03 10:09:04 -07:00
return clients.filter(function (c) { return user.admin || (c.activeTokens && c.activeTokens.length > 0); });
2018-01-22 13:01:38 -08:00
};
});
app.filter('prettyAppMessage', function () {
return function (message) {
if (message === 'ETRYAGAIN') return 'The DNS record for this location is not setup correctly. Please verify your DNS settings and repair this app.';
if (message === 'DNS Record already exists') return 'The DNS record for this location already exists. Manually remove the DNS record and then click on repair.';
return message;
};
});
app.filter('shortAppMessage', function () {
return function (message) {
if (message === 'ETRYAGAIN') return 'DNS record not setup correctly';
return message;
};
});
app.filter('prettyMemory', function () {
return function (memory) {
// Adjust the default memory limit if it changes
return memory ? Math.floor(memory / 1024 / 1024) : 256;
};
});
app.filter('installationActive', function () {
return function(app) {
if (app.installationState === ISTATES.ERROR) return false;
if (app.installationState === ISTATES.INSTALLED) return false;
return true;
};
});
app.filter('installationStateLabel', function() {
// for better DNS errors
function detailedError(app) {
if (app.installationProgress === 'ETRYAGAIN') return 'DNS Error';
return 'Error';
}
return function(app) {
var waiting = app.progress === 0 ? ' (Pending)' : '';
switch (app.installationState) {
case ISTATES.PENDING_INSTALL:
case ISTATES.PENDING_CLONE:
return 'Installing' + waiting;
case ISTATES.PENDING_CONFIGURE: return 'Configuring' + waiting;
case ISTATES.PENDING_UNINSTALL: return 'Uninstalling' + waiting;
case ISTATES.PENDING_RESTORE: return 'Restoring' + waiting;
case ISTATES.PENDING_UPDATE: return 'Updating' + waiting;
case ISTATES.PENDING_FORCE_UPDATE: return 'Updating' + waiting;
case ISTATES.PENDING_BACKUP: return 'Backing up' + waiting;
case ISTATES.ERROR: return detailedError(app);
case ISTATES.INSTALLED: {
if (app.debugMode) {
return app.debugMode.readonlyRootfs ? 'Paused (Repair)' : 'Paused (Debug)';
} else if (app.runState === 'running') {
2018-01-22 13:01:38 -08:00
if (!app.health) return 'Starting...'; // no data yet
if (app.health === HSTATES.HEALTHY) return 'Running';
return 'Not responding'; // dead/exit/unhealthy
} else if (app.runState === 'pending_start') return 'Starting...';
else if (app.runState === 'pending_stop') return 'Stopping...';
else if (app.runState === 'stopped') return 'Stopped';
else return app.runState;
}
default: return app.installationState;
}
};
});
app.filter('readyToUpdate', function () {
return function (apps) {
return apps.every(function (app) {
return (app.installationState === ISTATES.ERROR) || (app.installationState === ISTATES.INSTALLED);
});
};
});
app.filter('inProgressApps', function () {
return function (apps) {
return apps.filter(function (app) {
return app.installationState !== ISTATES.ERROR && app.installationState !== ISTATES.INSTALLED;
});
};
});
app.filter('applicationLink', function() {
return function(app) {
if (app.installationState === ISTATES.INSTALLED && app.health === HSTATES.HEALTHY && !app.pendingPostInstallConfirmation) {
2018-01-22 13:01:38 -08:00
return 'https://' + app.fqdn;
} else {
return '';
}
};
});
app.filter('prettyHref', function () {
return function (input) {
if (!input) return input;
if (input.indexOf('http://') === 0) return input.slice('http://'.length);
if (input.indexOf('https://') === 0) return input.slice('https://'.length);
return input;
};
});
app.filter('prettyDate', function () {
// http://ejohn.org/files/pretty.js
return function prettyDate(time) {
var date = new Date(time),
diff = (((new Date()).getTime() - date.getTime()) / 1000) + 30, // add 30seconds for clock skew
day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff) || day_diff < 0)
return 'just now';
return day_diff === 0 && (
diff < 60 && 'just now' ||
diff < 120 && '1 minute ago' ||
diff < 3600 && Math.floor( diff / 60 ) + ' minutes ago' ||
diff < 7200 && '1 hour ago' ||
diff < 86400 && Math.floor( diff / 3600 ) + ' hours ago') ||
day_diff === 1 && 'Yesterday' ||
day_diff < 7 && day_diff + ' days ago' ||
day_diff < 31 && Math.ceil( day_diff / 7 ) + ' weeks ago' ||
day_diff < 365 && Math.round( day_diff / 30 ) + ' months ago' ||
Math.round( day_diff / 365 ) + ' years ago';
};
});
app.filter('prettyLongDate', function () {
return function prettyLongDate(time) {
return moment(time).format('MMMM Do YYYY, h:mm:ss a');
};
});
2018-01-22 13:01:38 -08:00
app.filter('markdown2html', function () {
var converter = new showdown.Converter({
extensions: ['targetblank'],
simplifiedAutoLink: true,
strikethrough: true,
tables: true
});
return function (text) {
return converter.makeHtml(text);
};
});
app.filter('postInstallMessage', function () {
var SSO_MARKER = '=== sso ===';
return function (text, app) {
if (!text) return '';
if (!app) return text;
var parts = text.split(SSO_MARKER);
if (parts.length === 1) {
// [^] matches even newlines. '?' makes it non-greedy
if (app.sso) return text.replace(/\<nosso\>[^]*?\<\/nosso\>/g, '');
else return text.replace(/\<sso\>[^]*?\<\/sso\>/g, '');
}
if (app.sso) return parts[1];
else return parts[0];
};
});
// keep this in sync with eventlog.js and CLI tool
var ACTION_ACTIVATE = 'cloudron.activate';
2018-11-10 01:34:50 -08:00
var ACTION_PROVISION = 'cloudron.provision';
var ACTION_RESTORE = 'cloudron.restore';
2019-01-11 12:27:27 -08:00
var ACTION_APP_CLONE = 'app.clone';
2018-01-22 13:01:38 -08:00
var ACTION_APP_CONFIGURE = 'app.configure';
var ACTION_APP_INSTALL = 'app.install';
var ACTION_APP_RESTORE = 'app.restore';
var ACTION_APP_UNINSTALL = 'app.uninstall';
var ACTION_APP_UPDATE = 'app.update';
var ACTION_APP_LOGIN = 'app.login';
2019-01-17 17:23:34 +01:00
var ACTION_APP_OOM = 'app.oom';
var ACTION_APP_DOWN = 'app.down';
2019-01-19 15:53:49 +01:00
var ACTION_APP_TASK_CRASH = 'app.task.crash';
2018-01-22 13:01:38 -08:00
var ACTION_BACKUP_FINISH = 'backup.finish';
var ACTION_BACKUP_START = 'backup.start';
2019-01-11 12:27:27 -08:00
var ACTION_BACKUP_CLEANUP_START = 'backup.cleanup.start';
var ACTION_BACKUP_CLEANUP_FINISH = 'backup.cleanup.finish';
2018-11-14 20:37:58 -08:00
var ACTION_CERTIFICATE_NEW = 'certificate.new';
2018-01-22 13:01:38 -08:00
var ACTION_CERTIFICATE_RENEWAL = 'certificate.renew';
2018-11-10 01:09:06 -08:00
var ACTION_DOMAIN_ADD = 'domain.add';
var ACTION_DOMAIN_UPDATE = 'domain.update';
var ACTION_DOMAIN_REMOVE = 'domain.remove';
2018-01-22 13:01:38 -08:00
var ACTION_START = 'cloudron.start';
var ACTION_UPDATE = 'cloudron.update';
var ACTION_USER_ADD = 'user.add';
var ACTION_USER_LOGIN = 'user.login';
var ACTION_USER_REMOVE = 'user.remove';
var ACTION_USER_UPDATE = 'user.update';
2018-07-05 13:54:05 -07:00
var ACTION_USER_TRANSFER = 'user.transfer';
2018-01-22 13:01:38 -08:00
2018-11-10 00:32:37 -08:00
var ACTION_MAIL_ENABLED = 'mail.enabled';
var ACTION_MAIL_DISABLED = 'mail.disabled';
var ACTION_MAIL_MAILBOX_ADD = 'mail.box.add';
var ACTION_MAIL_MAILBOX_REMOVE = 'mail.box.remove';
var ACTION_MAIL_LIST_ADD = 'mail.list.add';
var ACTION_MAIL_LIST_REMOVE = 'mail.list.remove';
2019-01-12 09:58:30 -08:00
var ACTION_DYNDNS_UPDATE = 'dyndns.update';
2019-01-19 15:53:49 +01:00
var ACTION_SYSTEM_CRASH = 'system.crash';
2018-03-02 19:21:24 +01:00
app.filter('eventLogSource', function() {
return function(eventLog) {
var source = eventLog.source;
// var data = eventLog.data;
// var errorMessage = data.errorMessage;
2019-01-17 17:23:34 +01:00
var line = '';
2018-03-02 19:21:24 +01:00
// <span ng-show="eventLog.source.ip || eventLog.source.appId"> ({{ eventLog.source.ip || eventLog.source.appId }}) </span>
2019-01-17 17:23:34 +01:00
if (source.app) {
line = (source.app.fqdn || (source.app.location + '.' + source.app.domain));
} else {
line = source.username || source.userId || source.authType || 'system';
if (source.ip) line += ' - ' + source.ip;
if (source.appId) line += ' - ' + source.appId;
}
2018-03-02 19:21:24 +01:00
return line;
};
});
2018-01-22 13:01:38 -08:00
app.filter('eventLogDetails', function() {
// NOTE: if you change this, the CLI tool (cloudron machine eventlog) probably needs fixing as well
return function(eventLog) {
var source = eventLog.source;
var data = eventLog.data;
var errorMessage = data.errorMessage;
2019-01-11 12:27:27 -08:00
var details;
2018-01-22 13:01:38 -08:00
2018-03-02 10:49:46 +01:00
switch (eventLog.action) {
2018-03-13 21:56:47 -07:00
case ACTION_ACTIVATE:
return 'Cloudron was activated';
2018-11-10 01:34:50 -08:00
case ACTION_PROVISION:
return 'Cloudron was setup';
case ACTION_RESTORE:
return 'Cloudron was restored from backup ' + data.backupId;
2018-03-13 21:56:47 -07:00
case ACTION_APP_CONFIGURE:
2019-01-11 12:27:27 -08:00
if (!data.app) return '';
return data.app.manifest.title + ' was re-configured at ' + (data.app.fqdn || data.app.location);
2018-03-13 21:56:47 -07:00
case ACTION_APP_INSTALL:
2019-01-11 12:27:27 -08:00
if (!data.app) return '';
return data.app.manifest.title + ' was installed at ' + (data.app.fqdn || data.app.location) + ' with version ' + data.app.manifest.version;
2018-03-13 21:56:47 -07:00
case ACTION_APP_RESTORE:
2019-01-11 12:27:27 -08:00
if (!data.app) return '';
details = data.app.manifest.title + ' was restored at ' + (data.app.fqdn || data.app.location);
// older versions (<3.5) did not have these fields
2019-01-13 14:47:12 -08:00
if (data.fromManifest) details += ' from version ' + data.fromManifest.version;
if (data.toManifest) details += ' to version ' + data.toManifest.version;
if (data.backupId) details += ' using backup ' + data.backupId;
2019-01-11 12:27:27 -08:00
return details;
2018-03-13 21:56:47 -07:00
case ACTION_APP_UNINSTALL:
2019-01-11 12:27:27 -08:00
if (!data.app) return '';
return data.app.manifest.title + ' was uninstalled at ' + (data.app.fqdn || data.app.location) + ' with version ' + data.app.manifest.version;
2018-03-13 21:56:47 -07:00
case ACTION_APP_UPDATE:
2019-01-11 12:27:27 -08:00
if (!data.app) return '';
return data.app.manifest.title + ' at ' + (data.app.fqdn || data.app.location) + ' was updated from version ' + data.fromManifest.version + ' to version ' + data.toManifest.version;
case ACTION_APP_CLONE:
return data.newApp.manifest.title + ' at ' + (data.newApp.fqdn || data.newApp.location) + ' was cloned from ' + (data.oldApp.fqdn || data.oldApp.location) + ' from backup ' + data.backupId + ' with version ' + data.oldApp.manifest.version;
2018-03-13 21:56:47 -07:00
case ACTION_APP_LOGIN:
return 'App ' + data.appId + ' logged in';
2019-01-17 17:23:34 +01:00
case ACTION_APP_OOM:
return source.app.manifest.title + ' ran out of memory';
case ACTION_APP_DOWN:
2019-01-17 17:27:26 +01:00
return source.app.manifest.title + ' is not reachable';
2019-01-17 17:23:34 +01:00
2019-01-19 15:53:49 +01:00
case ACTION_APP_TASK_CRASH:
return 'Apptask for app with id ' + source.appId + ' crashed';
2018-03-13 21:56:47 -07:00
case ACTION_BACKUP_START:
return 'Backup started';
case ACTION_BACKUP_FINISH:
return 'Backup finished' + (errorMessage ? (' error: ' + errorMessage) : '');
2019-01-11 12:27:27 -08:00
case ACTION_BACKUP_CLEANUP_START:
return 'Backup cleaner started';
case ACTION_BACKUP_CLEANUP_FINISH:
2019-01-12 10:08:32 -08:00
return data.errorMessage ? 'Backup cleaner errored: ' + data.errorMessage : 'Backup cleaner removed ' + data.removedBoxBackups.length + ' backups';
2018-03-13 21:56:47 -07:00
2018-11-14 20:37:58 -08:00
case ACTION_CERTIFICATE_NEW:
return 'Certificate install for ' + data.domain + (errorMessage ? ' failed' : ' succeeded');
2018-03-13 21:56:47 -07:00
case ACTION_CERTIFICATE_RENEWAL:
return 'Certificate renewal for ' + data.domain + (errorMessage ? ' failed' : ' succeeded');
2018-11-10 01:09:06 -08:00
case ACTION_DOMAIN_ADD:
return 'Domain ' + data.domain + ' with ' + data.provider + ' provider was added';
2018-03-13 21:56:47 -07:00
2018-11-10 01:09:06 -08:00
case ACTION_DOMAIN_UPDATE:
return 'Domain ' + data.domain + ' with ' + data.provider + ' provider was updated';
2018-07-05 13:54:05 -07:00
2018-11-10 01:09:06 -08:00
case ACTION_DOMAIN_REMOVE:
return 'Domain ' + data.domain + ' was removed';
2018-03-13 21:56:47 -07:00
2018-11-10 00:32:37 -08:00
case ACTION_MAIL_ENABLED:
return 'Cloudron Mail was enabled for domain ' + data.domain;
case ACTION_MAIL_DISABLED:
return 'Cloudron Mail was disabled for domain ' + data.domain;
case ACTION_MAIL_MAILBOX_ADD:
return 'Mailbox with name ' + data.name + ' was added in domain ' + data.domain;
case ACTION_MAIL_MAILBOX_REMOVE:
return 'Mailbox with name ' + data.name + ' was removed in domain ' + data.domain;
case ACTION_MAIL_LIST_ADD:
return 'Mail list with name ' + data.name + ' was added in domain ' + data.domain;
case ACTION_MAIL_LIST_REMOVE:
return 'Mail list with name ' + data.name + ' was added in domain ' + data.domain;
2018-11-10 01:09:06 -08:00
case ACTION_START:
return 'Cloudron started with version ' + data.version;
case ACTION_UPDATE:
return 'Cloudron was updated to version ' + data.boxUpdateInfo.version;
case ACTION_USER_ADD:
return data.email + (data.user.username ? ' (' + data.user.username + ')' : '') + ' was added';
case ACTION_USER_UPDATE:
return (data.user ? (data.user.email + (data.user.username ? ' (' + data.user.username + ')' : '')) : data.userId) + ' was updated';
case ACTION_USER_REMOVE:
return (data.user ? (data.user.email + (data.user.username ? ' (' + data.user.username + ')' : '')) : data.userId) + ' was removed';
case ACTION_USER_TRANSFER:
return 'Apps of ' + data.oldOwnerId + ' was transferred to ' + data.newOwnerId;
case ACTION_USER_LOGIN:
return (data.user ? (data.user.email + (data.user.username ? ' (' + data.user.username + ')' : '')) : data.userId) + ' logged in';
2019-01-12 09:58:30 -08:00
case ACTION_DYNDNS_UPDATE:
return 'DNS was updated from ' + data.fromIp + ' to ' + data.toIp;
2019-01-19 15:53:49 +01:00
case ACTION_SYSTEM_CRASH:
return 'A system process crashed';
2018-03-13 21:56:47 -07:00
default: return eventLog.action;
2018-02-19 01:56:09 -08:00
}
2018-03-02 10:49:46 +01:00
};
});
app.filter('eventLogAction', function() {
return function(eventLog) {
2018-01-22 13:01:38 -08:00
switch (eventLog.action) {
2018-03-13 21:56:47 -07:00
case ACTION_ACTIVATE: return 'Cloudron activated';
2018-11-10 01:34:50 -08:00
case ACTION_RESTORE: return 'Cloudron restored';
case ACTION_PROVISION: return 'Cloudron provisioned';
2018-03-13 21:56:47 -07:00
case ACTION_APP_CONFIGURE: return 'App configured';
case ACTION_APP_INSTALL: return 'App installed';
case ACTION_APP_RESTORE: return 'App restored';
case ACTION_APP_UNINSTALL: return 'App uninstalled';
case ACTION_APP_UPDATE: return 'App updated';
2019-01-11 12:27:27 -08:00
case ACTION_APP_CLONE: return 'App cloned';
2018-03-13 21:56:47 -07:00
case ACTION_APP_LOGIN: return 'App login';
case ACTION_BACKUP_START: return 'Backup started';
case ACTION_BACKUP_FINISH: return 'Backup finished';
2019-01-11 12:27:27 -08:00
case ACTION_BACKUP_CLEANUP_START: return 'Backup cleaner started';
case ACTION_BACKUP_CLEANUP_FINISH: return 'Backup cleaner finished';
case ACTION_CERTIFICATE_NEW: return 'Certificated installed';
2018-03-13 21:56:47 -07:00
case ACTION_CERTIFICATE_RENEWAL: return 'Certificate renewal';
2018-11-10 01:09:06 -08:00
case ACTION_DOMAIN_ADD: return 'Domain added';
case ACTION_DOMAIN_UPDATE: return 'Domain updated';
case ACTION_DOMAIN_REMOVE: return 'Domain removed';
2018-11-10 00:32:37 -08:00
case ACTION_MAIL_ENABLED: return 'Mail enabled';
case ACTION_MAIL_DISABLED: return 'Mail disabled';
case ACTION_MAIL_MAILBOX_ADD: return 'Mailbox added';
case ACTION_MAIL_MAILBOX_REMOVE: return 'Mailbox removed';
case ACTION_MAIL_LIST_ADD: return 'Mail list added';
case ACTION_MAIL_LIST_REMOVE: return 'Mail list removed';
2018-11-10 01:09:06 -08:00
case ACTION_START: return 'Cloudron started';
2019-01-11 12:27:27 -08:00
case ACTION_UPDATE: return 'Cloudron updated';
2018-11-10 01:09:06 -08:00
case ACTION_USER_ADD: return 'User added';
case ACTION_USER_LOGIN: return 'User login';
case ACTION_USER_REMOVE: return 'User removed';
case ACTION_USER_UPDATE: return 'User updated';
2019-01-12 09:58:30 -08:00
case ACTION_DYNDNS_UPDATE: return 'DNS Updated';
2018-03-13 21:56:47 -07:00
default: return eventLog.action;
2018-01-22 13:01:38 -08:00
}
};
});
// custom directive for dynamic names in forms
// See http://stackoverflow.com/questions/23616578/issue-registering-form-control-with-interpolated-name#answer-23617401
app.directive('laterName', function () { // (2)
return {
restrict: 'A',
require: ['?ngModel', '^?form'], // (3)
link: function postLink(scope, elem, attrs, ctrls) {
attrs.$set('name', attrs.laterName);
var modelCtrl = ctrls[0]; // (3)
var formCtrl = ctrls[1]; // (3)
if (modelCtrl && formCtrl) {
modelCtrl.$name = attrs.name; // (4)
formCtrl.$addControl(modelCtrl); // (2)
scope.$on('$destroy', function () {
formCtrl.$removeControl(modelCtrl); // (5)
});
}
}
};
});
app.run(['$route', '$rootScope', '$location', function ($route, $rootScope, $location) {
var original = $location.path;
$location.path = function (path, reload) {
if (reload === false) {
var lastRoute = $route.current;
var un = $rootScope.$on('$locationChangeSuccess', function () {
$route.current = lastRoute;
un();
});
}
return original.apply($location, [path]);
};
}]);
app.directive('ngClickSelect', function () {
return {
restrict: 'AC',
link: function (scope, element/*, attrs */) {
2018-01-22 13:01:38 -08:00
element.bind('click', function () {
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(this);
selection.removeAllRanges();
selection.addRange(range);
});
}
};
});
app.directive('ngClickReveal', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.addClass('hand');
var value = '';
scope.$watch(attrs.ngClickReveal, function (newValue, oldValue) {
if (newValue !== oldValue) {
element.html('<i>hidden</i>');
value = newValue;
}
});
element.bind('click', function () {
element.text(value);
});
}
};
});
// https://codepen.io/webmatze/pen/isuHh
app.directive('tagInput', function () {
return {
restrict: 'E',
scope: {
inputTags: '=taglist'
},
link: function ($scope, element, attrs) {
$scope.defaultWidth = 200;
$scope.tagText = ''; // current tag being edited
$scope.placeholder = attrs.placeholder;
$scope.tagArray = function () {
if ($scope.inputTags === undefined) {
return [];
}
return $scope.inputTags.split(',').filter(function (tag) {
return tag !== '';
});
};
$scope.addTag = function () {
var tagArray;
if ($scope.tagText.length === 0) {
return;
}
tagArray = $scope.tagArray();
tagArray.push($scope.tagText);
$scope.inputTags = tagArray.join(',');
return $scope.tagText = '';
};
$scope.deleteTag = function (key) {
var tagArray;
tagArray = $scope.tagArray();
if (tagArray.length > 0 && $scope.tagText.length === 0 && key === undefined) {
tagArray.pop();
} else {
if (key !== undefined) {
tagArray.splice(key, 1);
}
}
return $scope.inputTags = tagArray.join(',');
};
$scope.$watch('tagText', function (newVal, oldVal) {
var tempEl;
if (!(newVal === oldVal && newVal === undefined)) {
tempEl = $('<span>' + newVal + '</span>').appendTo('body');
$scope.inputWidth = tempEl.width() + 5;
if ($scope.inputWidth < $scope.defaultWidth) {
$scope.inputWidth = $scope.defaultWidth;
}
return tempEl.remove();
}
});
element.bind('keydown', function (e) {
var key = e.which;
if (key === 9 || key === 13) {
e.preventDefault();
}
if (key === 8) {
return $scope.$apply('deleteTag()');
}
});
element.bind('keyup', function (e) {
var key = e.which;
if (key === 9 || key === 13 || key === 32 || key === 188) {
e.preventDefault();
return $scope.$apply('addTag()');
}
});
},
template:
'<div class="tag-input-container">' +
'<div class="input-tag" data-ng-repeat="tag in tagArray()">' +
'{{tag}}' +
'<div class="delete-tag" data-ng-click="deleteTag($index)">&times;</div>' +
'</div>' +
'<input type="text" data-ng-model="tagText" ng-blur="addTag()" placeholder="{{placeholder}}"/>' +
'</div>'
};
});
app.config(['fitTextConfigProvider', function (fitTextConfigProvider) {
fitTextConfigProvider.config = {
loadDelay: 250,
compressor: 0.9,
min: 8,
max: 24
};
}]);