diff --git a/ajax-wrapper/AjaxWrapper.php b/ajax-wrapper/AjaxWrapper.php
new file mode 100644
index 0000000..79c730f
--- /dev/null
+++ b/ajax-wrapper/AjaxWrapper.php
@@ -0,0 +1,457 @@
+action = $action;
+ }
+
+ /**
+ * @param callable $callback
+ * @return $this
+ */
+ public function handler($callback) {
+ $this->callback = $callback;
+ return $this;
+ }
+
+ public function requiredParam($name, $type = null, $validateCallback = null) {
+ return $this->addParameter($name, $type, true, null, $validateCallback);
+ }
+
+ public function optionalParam($name, $defaultValue = null, $type = null, $validateCallback = null) {
+ return $this->addParameter($name, $type, false, $defaultValue, $validateCallback);
+ }
+
+ private function addParameter($name, $type, $required, $defaultValue, $validateCallback) {
+ if (isset($type) && !isset(Ajaw_v1_Action::$defaultValidators[$type])) {
+ throw new LogicException(sprintf(
+ 'Unknown parameter type "%s". Supported types are: %s.',
+ $type,
+ implode(', ', array_keys(Ajaw_v1_Action::$defaultValidators[$type]))
+ ));
+ }
+
+ $this->params[$name] = array(
+ 'required' => $required,
+ 'defaultValue' => $defaultValue,
+ 'type' => $type,
+ 'validateCallback' => $validateCallback,
+ );
+ return $this;
+ }
+
+ public function method($httpMethod) {
+ $this->httpMethod = strtoupper($httpMethod);
+ return $this;
+ }
+
+ public function requiredCap($capability) {
+ $this->capability = $capability;
+ return $this;
+ }
+
+ public function permissionCallback($callback) {
+ $this->permissionCheckCallback = $callback;
+ return $this;
+ }
+
+ public function allowUnprivilegedUsers() {
+ $this->mustBeLoggedIn = false;
+ return $this;
+ }
+
+ public function withoutNonce() {
+ $this->checkNonce = false;
+ return $this;
+ }
+
+ public function build() {
+ $instance = new Ajaw_v1_Action($this->action, $this->callback, $this->params);
+
+ $instance->mustBeLoggedIn = $this->mustBeLoggedIn;
+ $instance->requiredCap = $this->capability;
+ $instance->nonceCheckEnabled = $this->checkNonce;
+ $instance->method = $this->httpMethod;
+ $instance->permissionCallback = $this->permissionCheckCallback;
+
+ return $instance;
+ }
+
+ public function register() {
+ $instance = $this->build();
+ $instance->register();
+ return $instance;
+ }
+ }
+
+endif;
+
+if (!class_exists('Ajaw_v1_Action', false)):
+
+ class Ajaw_v1_Action {
+ public $action;
+ public $callback;
+ public $params = array();
+ public $method = null;
+
+ public $requiredCap = null;
+ public $mustBeLoggedIn = false;
+ public $nonceCheckEnabled = true;
+ public $permissionCallback = null;
+
+ private $isScriptRegistered = false;
+
+ public static $defaultValidators = array(
+ 'int' => array(__CLASS__, 'validateInt'),
+ 'float' => array(__CLASS__, 'validateFloat'),
+ 'boolean' => array(__CLASS__, 'validateBoolean'),
+ 'string' => array(__CLASS__, 'validateString'),
+ );
+
+ public function __construct($action, $callback, $params) {
+ $this->action = $action;
+ $this->callback = $callback;
+ $this->params = $params;
+
+ if (empty($this->action)) {
+ throw new LogicException(sprintf(
+ 'AJAX action name is missing. You must either pass it to the %1$s constructor '
+ . 'or give the %1$s::$action property a valid default value.',
+ get_class($this)
+ ));
+ }
+ }
+
+ /**
+ * Set up hooks for AJAX and helper scripts.
+ */
+ public function register() {
+ //Register the AJAX handler(s).
+ $hookNames = array('wp_ajax_' . $this->action);
+ if (!$this->mustBeLoggedIn) {
+ $hookNames[] = 'wp_ajax_nopriv_' . $this->action;
+ }
+
+ foreach($hookNames as $hook) {
+ if (has_action($hook)) {
+ throw new RuntimeException(sprintf('The action name "%s" is already in use.', $this->action));
+ }
+ add_action($hook, array($this, 'processAjaxRequest'));
+ }
+
+ //Register the utility JS library after WP is fully loaded.
+ if (did_action('wp_loaded')) {
+ $this->registerScript();
+ } else {
+ add_action('wp_loaded', array($this, 'registerScript'), 2);
+ }
+ }
+
+ /**
+ * @access protected
+ */
+ public function processAjaxRequest() {
+ $result = $this->handleAction();
+
+ if (is_wp_error($result)) {
+ $statusCode = $result->get_error_data();
+ if (isset($statusCode) && is_int($statusCode) ) {
+ status_header($statusCode);
+ }
+
+ $errorResponse = array(
+ 'error' => array(
+ 'message' => $result->get_error_message(),
+ 'code' => $result->get_error_code()
+ )
+ );
+
+ $result = $errorResponse;
+ }
+
+ if (isset($result)) {
+ $this->outputJSON($result);
+ }
+ exit;
+ }
+
+ protected function handleAction() {
+ $method = $this->getRequestMethod();
+ if (isset($this->method) && ($method !== $this->method)) {
+ return new WP_Error(
+ 'http_method_not_allowed',
+ 'The HTTP method is not supported by the request handler.',
+ 405
+ );
+ }
+
+ $isAuthorized = $this->checkAuthorization();
+ if ($isAuthorized !== true) {
+ return $isAuthorized;
+ }
+
+ $params = $this->parseParameters();
+ if ($params instanceof WP_Error) {
+ return $params;
+ }
+
+ //Call the user-specified action handler.
+ if (is_callable($this->callback)) {
+ return call_user_func($this->callback, $params);
+ } else {
+ return new WP_Error(
+ 'missing_ajax_handler',
+ sprintf(
+ 'There is no request handler assigned to the "%1$s" action. '
+ . 'Either pass a valid callback to $builder->request() or override the %2$s::%3$s method.',
+ $this->action,
+ __CLASS__,
+ __METHOD__
+ ),
+ 500
+ );
+ }
+ }
+
+ /**
+ * Check if the current user is authorized to perform this action.
+ *
+ * @return bool|WP_Error
+ */
+ protected function checkAuthorization() {
+ if ($this->mustBeLoggedIn && !is_user_logged_in()) {
+ return new WP_Error('login_required', 'You must be logged in to perform this action.', 403);
+ }
+
+ if (isset($this->requiredCap) && !current_user_can($this->requiredCap)) {
+ return new WP_Error('capability_missing', 'You don\'t have permission to perform this action.', 403);
+ }
+
+ if ($this->nonceCheckEnabled && !check_ajax_referer($this->action, false, false)) {
+ return new WP_Error('nonce_check_failed', 'Invalid or missing nonce.', 403);
+ }
+
+ if (isset($this->permissionCallback)) {
+ $result = call_user_func($this->permissionCallback);
+ if ($result === false) {
+ return new WP_Error(
+ 'permission_callback_failed',
+ 'You don\'t have permission to perform this action.',
+ 403
+ );
+ } else if (is_wp_error($result)) {
+ return $result;
+ }
+ }
+
+ return true;
+ }
+
+ protected function getRequestMethod() {
+ return strtoupper(filter_input(
+ INPUT_SERVER,
+ 'REQUEST_METHOD',
+ FILTER_VALIDATE_REGEXP,
+ array('options' => array('regexp' => '/^[a-z]{3,20}$/i'))
+ ));
+ }
+
+ protected function parseParameters() {
+ $method = $this->getRequestMethod();
+
+ // phpcs:disable WordPress.Security.NonceVerification -- checkAuthorization() is where nonce verification happens.
+ //Retrieve request parameters.
+ if ($method === 'GET') {
+ $rawParams = $_GET;
+ } else if ($method === 'POST') {
+ $rawParams = $_POST;
+ } else {
+ $rawParams = $_REQUEST;
+ }
+ // phpcs:enable
+
+ //Remove magic quotes. WordPress applies them in wp-settings.php.
+ //There's no hook for wp_magic_quotes, so we use one that's closest in execution order.
+ if (did_action('sanitize_comment_cookies') && function_exists('wp_magic_quotes')) {
+ $rawParams = wp_unslash($rawParams);
+ }
+
+ //Validate all parameters.
+ $inputParams = $rawParams;
+ foreach($this->params as $name => $settings) {
+ //Verify that all the required parameters are present.
+ //Empty strings are treated as missing parameters.
+ if (isset($inputParams[$name]) && ($inputParams[$name] !== '')) {
+ $value = $this->validateParameter($settings, $inputParams[$name], $name);
+ if (is_wp_error($value)) {
+ return $value;
+ } else {
+ $inputParams[$name] = $value;
+ }
+ } else if (empty($settings['required'])) {
+ //It's an optional parameter. Use the default value.
+ $inputParams[$name] = $settings['defaultValue'];
+ } else {
+ return new WP_Error(
+ 'missing_required_parameter',
+ sprintf('Required parameter is missing or empty: "%s".', $name),
+ 400
+ );
+ }
+ }
+
+ return $inputParams;
+ }
+
+ protected function validateParameter($settings, $value, $name) {
+ if (isset($settings['type'])) {
+ $value = call_user_func(self::$defaultValidators[$settings['type']], $value, $name);
+ if (is_wp_error($value)) {
+ return $value;
+ }
+ }
+ if (isset($settings['validateCallback'])) {
+ $success = call_user_func($settings['validateCallback'], $value);
+ if (is_wp_error($success)) {
+ return $success;
+ } else if ($success === false) {
+ return new WP_Error(
+ 'invalid_parameter_value',
+ sprintf('The value of the parameter "%s" is invalid.', $name),
+ 400
+ );
+ }
+ }
+ return $value;
+ }
+
+ private static function validateInt($value, $name) {
+ $result = filter_var($value, FILTER_VALIDATE_INT);
+ if ($result === false) {
+ return new WP_Error(
+ 'invalid_parameter_value',
+ sprintf('The value of the parameter "%s" is invalid. It must be an integer.', $name),
+ 400
+ );
+ }
+ return $result;
+ }
+
+ private static function validateFloat($value, $name) {
+ $result = filter_var($value, FILTER_VALIDATE_FLOAT);
+ if ($result === false) {
+ return new WP_Error(
+ 'invalid_parameter_value',
+ sprintf('The value of the parameter "%s" is invalid. It must be a float.', $name),
+ 400
+ );
+ }
+ return $result;
+ }
+
+ private static function validateBoolean($value, $name) {
+ $result = filter_var($value, FILTER_VALIDATE_BOOLEAN, array('flags' => FILTER_NULL_ON_FAILURE));
+ if ($result === null) {
+ return new WP_Error(
+ 'invalid_parameter_value',
+ sprintf('The value of the parameter "%s" is invalid. It must be a boolean.', $name),
+ 400
+ );
+ }
+ return $result;
+ }
+
+ private static function validateString($value, $name) {
+ if (!is_string($value)) {
+ return new WP_Error(
+ 'invalid_parameter_value',
+ sprintf('The value of the parameter "%s" is invalid. It must be a string.', $name),
+ 400
+ );
+ }
+ return $value;
+ }
+
+ protected function outputJSON($response) {
+ @header('Content-Type: application/json; charset=' . get_option('blog_charset'));
+ echo wp_json_encode($response);
+ }
+
+ public function registerScript() {
+ if ($this->isScriptRegistered) {
+ return;
+ }
+ $this->isScriptRegistered = true;
+
+ //There could be multiple instances of this class, but we only need to register the script once.
+ $handle = $this->getScriptHandle();
+ if (!wp_script_is($handle, 'registered')) {
+ wp_register_script(
+ $handle,
+ plugins_url('ajax-action-wrapper.js', __FILE__),
+ array('jquery'),
+ '20161105'
+ );
+ }
+
+ //Pass the action to the script.
+ if (function_exists('wp_add_inline_script')) {
+ wp_add_inline_script($handle, $this->generateActionJs(), 'after'); //WP 4.5+
+ } else {
+ add_filter('script_loader_tag', array($this, 'addRegistrationScript'), 10, 2); //WP 4.1+
+ }
+ }
+
+ /**
+ * Backwards compatibility for older versions of WP that don't have wp_add_inline_script().
+ * @internal
+ *
+ * @param string $tag
+ * @param string $handle
+ * @return string
+ */
+ public function addRegistrationScript($tag, $handle) {
+ if ($handle === $this->getScriptHandle()) {
+ $tag .= '';
+ }
+ return $tag;
+ }
+
+ protected function generateActionJs() {
+ $properties = array(
+ 'ajaxUrl' => admin_url('admin-ajax.php'),
+ 'method' => $this->method,
+ 'nonce' => $this->nonceCheckEnabled ? wp_create_nonce($this->action) : null,
+ );
+
+ return sprintf(
+ 'AjawV1.actionRegistry.add("%s", %s);' . "\n",
+ esc_js($this->action),
+ wp_json_encode($properties)
+ );
+ }
+
+ public function getScriptHandle() {
+ return 'ajaw-v1-ajax-action-wrapper';
+ }
+ }
+
+endif;
+
+if (!function_exists('ajaw_v1_CreateAction')) {
+ function ajaw_v1_CreateAction($action) {
+ return new Ajaw_v1_ActionBuilder($action);
+ }
+}
diff --git a/ajax-wrapper/README.md b/ajax-wrapper/README.md
new file mode 100644
index 0000000..16d6f14
--- /dev/null
+++ b/ajax-wrapper/README.md
@@ -0,0 +1,74 @@
+# AJAX Action Wrapper
+
+This helper library makes it easier to handle AJAX requests in WordPress plugins. Mainly for personal use.
+
+### Example
+Define action:
+```php
+$exampleAction = ajaw_v1_CreateAction('ws_do_something')
+ ->handler(array($this, 'myAjaxCallback'))
+ ->requiredCap('manage_options')
+ ->method('post')
+ ->requiredParam('foo')
+ ->optionalParam('bar', 'default value')
+ ->register();
+```
+
+Call from JavaScript:
+```javascript
+AjawV1.getAction('ws_do_something').post(
+ {
+ 'foo': '...'
+ },
+ function(response) {
+ console.log(response);
+ }
+);
+```
+
+### Features
+- Automate common, boring stuff.
+ - [x] Automatically pass the `admin-ajax.php` URL and nonce to JS.
+ - [x] Define required parameters.
+ ```php
+ $builder->requiredParam('foo', 'int')
+ ```
+ - [x] Define optional parameters with default values.
+ ```php
+ $builder->optionalParam('meaningOfLife', 42, 'int')
+ ```
+ - [x] Automatically remove "magic quotes" that WordPress adds to `$_GET`, `$_POST` and `$_REQUEST`.
+ - [x] Encode return values as JSON.
+- Security should be the default.
+ - [x] Generate and verify nonces. Nonce verification is on by default, but can be disabled.
+ ```php
+ $builder->withoutNonce()
+ ```
+ - [x] Check capabilities.
+ ```php
+ $builder->requiredCap('manage_options');
+ ```
+ - [x] Verify that all required parameters are set.
+ - [x] Validate parameter values.
+ ```php
+ $builder->optionalParam('things', 1, 'int', function($value) {
+ if ($value > 10) {
+ return new WP_Error(
+ 'excessive_things',
+ 'Too many things!',
+ 400 //HTTP status code.
+ );
+ }
+ })
+ ```
+ - [x] Set the required HTTP method.
+ ```php
+ $builder->method('post')
+ ```
+- Resilience.
+ - [ ] Lenient response parsing to work around bugs in other plugins. For example, deal with extraneous whitespace and PHP notices in AJAX responses.
+ - [x] Multiple versions of the library can coexist on the same site.
+
+### Why not use the REST API instead?
+
+Backwards compatibility. In theory, this library should be compatible with WP 4.1+.
\ No newline at end of file
diff --git a/ajax-wrapper/ajax-action-wrapper.d.ts b/ajax-wrapper/ajax-action-wrapper.d.ts
new file mode 100644
index 0000000..c5d7b42
--- /dev/null
+++ b/ajax-wrapper/ajax-action-wrapper.d.ts
@@ -0,0 +1,15 @@
+// Basic type definitions for the Ajaw AJAX wrapper library 1.0
+
+declare namespace AjawV1 {
+ interface RequestParams { [name: string]: any }
+ interface SuccessCallback { (data, textStatus: string, jqXHR): void }
+ interface ErrorCallback { (data, textStatus: string, jqXHR, errorThrown): void }
+
+ class AjawAjaxAction {
+ get(params?: RequestParams, success?: SuccessCallback, error?: ErrorCallback): void;
+ post(params?: RequestParams, success?: SuccessCallback, error?: ErrorCallback): void;
+ request(params?: RequestParams, success?: SuccessCallback, error?: ErrorCallback, method?: string): void;
+ }
+
+ function getAction(action: string): AjawAjaxAction;
+}
\ No newline at end of file
diff --git a/ajax-wrapper/ajax-action-wrapper.js b/ajax-wrapper/ajax-action-wrapper.js
new file mode 100644
index 0000000..23a5d1e
--- /dev/null
+++ b/ajax-wrapper/ajax-action-wrapper.js
@@ -0,0 +1,139 @@
+var AjawV1 = window.AjawV1 || {};
+
+AjawV1.AjaxAction = (function () {
+ "use strict";
+
+ function AjawAjaxAction(action, properties) {
+ this.action = action;
+ this.ajaxUrl = properties['ajaxUrl'];
+ this.nonce = properties['nonce'];
+ this.requiredMethod = (typeof properties['method'] !== 'undefined') ? properties['method'] : null;
+ }
+
+ /**
+ * Send a POST request.
+ *
+ * @param {Object} params
+ * @param {Function} success
+ * @param {Function} [error]
+ */
+ AjawAjaxAction.prototype.post = function (params, success, error) {
+ return this.request(params, success, error, 'POST');
+ };
+
+ /**
+ * Send a GET request.
+ *
+ * @param {Object} params
+ * @param {Function} success
+ * @param {Function} [error]
+ */
+ AjawAjaxAction.prototype.get = function(params, success, error) {
+ return this.request(params, success, error, 'GET');
+ };
+
+ /**
+ * Send an AJAX request using the specified HTTP method.
+ *
+ * @param {Object} params
+ * @param {Function} success
+ * @param {Function} [error]
+ * @param {String} [method]
+ */
+ AjawAjaxAction.prototype.request = function(params, success, error, method) {
+ if (typeof params === 'function') {
+ //It looks like "params" was omitted and the first argument is actually the success callback.
+ //Shift all arguments left one step. The reverse order is due to argument binding shenanigans.
+ method = arguments[2];
+ error = arguments[1];
+ success = arguments[0];
+ params = {};
+ }
+
+ if (typeof params === 'undefined') {
+ params = {};
+ } else if (typeof params !== 'object') {
+ //While jQuery accepts request data in object and string form, this library only supports objects.
+ throw 'Data that\'s to be sent to the server must be an object, not ' + (typeof params);
+ }
+
+ if (typeof method === 'undefined') {
+ method = this.requiredMethod || 'POST';
+ }
+ if (this.requiredMethod && (method !== this.requiredMethod)) {
+ throw 'Wrong HTTP method. This action requires ' + this.requiredMethod;
+ }
+
+ //noinspection JSUnusedGlobalSymbols
+ return jQuery.ajax(
+ this.ajaxUrl,
+ {
+ method: method,
+ data: this.prepareRequestParams(params),
+ success: function(data, textStatus, jqXHR) {
+ if (success) {
+ success(data, textStatus, jqXHR);
+ }
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ var data = jqXHR.responseText;
+ if (typeof jqXHR['responseJSON'] !== 'undefined') {
+ data = jqXHR['responseJSON'];
+ } else if (typeof jqXHR['responseXML'] !== 'undefined') {
+ data = jqXHR['responseXML'];
+ }
+
+ if (error) {
+ error(data, textStatus, jqXHR, errorThrown);
+ }
+ }
+ }
+ );
+ };
+
+ AjawAjaxAction.prototype.prepareRequestParams = function(params) {
+ if (params === null) {
+ params = {};
+ }
+
+ params['action'] = this.action;
+ if (this.nonce !== null) {
+ params['_ajax_nonce'] = this.nonce;
+ }
+ return params;
+ };
+
+ return AjawAjaxAction;
+}());
+
+AjawV1.actionRegistry = (function() {
+ var actions = {};
+
+ return {
+ /**
+ *
+ * @param {String} actionName
+ * @return {AjawAjaxAction}
+ */
+ get: function(actionName) {
+ if (actions.hasOwnProperty(actionName)) {
+ return actions[actionName];
+ }
+ return null;
+ },
+
+ add: function(actionName, properties) {
+ actions[actionName] = new AjawV1.AjaxAction(actionName, properties);
+ }
+ }
+})();
+
+/**
+ * Get a registered action wrapper.
+ *
+ * @param {string} action
+ * @return {AjawAjaxAction|null}
+ */
+AjawV1.getAction = function(action) {
+ return this.actionRegistry.get(action);
+};
\ No newline at end of file
diff --git a/css/_boxes.scss b/css/_boxes.scss
new file mode 100644
index 0000000..c81fb75
--- /dev/null
+++ b/css/_boxes.scss
@@ -0,0 +1,88 @@
+$amePostboxBorderColor: #ccd0d4; //Was #e5e5e5 before WP 5.3.
+$amePostboxShadow: 0 1px 1px rgba(0, 0, 0, 0.04);
+
+@mixin ame-emulated-postbox($toggleWidth: 36px, $horizontalPadding: 12px) {
+ $borderColor: $amePostboxBorderColor;
+ $headerBackground: #fff;
+
+ position: relative;
+ box-shadow: $amePostboxShadow;
+ background: $headerBackground;
+
+ margin-bottom: 20px;
+
+ .ws-ame-postbox-header {
+ position: relative;
+ font-size: 14px;
+ margin: 0;
+ line-height: 1.4;
+
+ border: 1px solid $borderColor;
+
+ h3 {
+ padding: 10px $horizontalPadding;
+ margin: 0;
+ font-size: 1em;
+ line-height: 1;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ }
+
+ .ws-ame-postbox-toggle {
+ color: #72777c;
+ background: $headerBackground;
+
+ display: block;
+ font: normal 20px/1 dashicons;
+ text-align: center;
+ cursor: pointer;
+ border: none;
+
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: $toggleWidth;
+ height: 100%;
+ padding: 0;
+
+ &:hover {
+ color: #23282d;
+ }
+
+ &:active, &:focus {
+ outline: none;
+ padding: 0;
+ }
+
+ &:before {
+ content: '\f142';
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ &:after {
+ display: inline-block;
+ content: "";
+ vertical-align: middle;
+ height: 100%;
+ }
+ }
+
+ .ws-ame-postbox-content {
+ border: 1px solid $borderColor;
+ border-top: none;
+
+ padding: $horizontalPadding;
+ }
+
+ &.ws-ame-closed-postbox .ws-ame-postbox-content {
+ display: none;
+ }
+
+ &.ws-ame-closed-postbox .ws-ame-postbox-toggle:before {
+ content: '\f140'; //downward triangle
+ }
+}
\ No newline at end of file
diff --git a/css/_dashicons.scss b/css/_dashicons.scss
new file mode 100644
index 0000000..e49d7ec
--- /dev/null
+++ b/css/_dashicons.scss
@@ -0,0 +1,224 @@
+/*
+This file was automatically generated from /wp-includes/css/dashicons.css.
+Last update: 2017-06-07T16:55:55+00:00
+*/
+.dashicons-menu:before { content: "\f333" !important; }
+.dashicons-admin-site:before { content: "\f319" !important; }
+.dashicons-admin-media:before { content: "\f104" !important; }
+.dashicons-admin-page:before { content: "\f105" !important; }
+.dashicons-admin-comments:before { content: "\f101" !important; }
+.dashicons-admin-appearance:before { content: "\f100" !important; }
+.dashicons-admin-plugins:before { content: "\f106" !important; }
+.dashicons-admin-users:before { content: "\f110" !important; }
+.dashicons-admin-tools:before { content: "\f107" !important; }
+.dashicons-admin-settings:before { content: "\f108" !important; }
+.dashicons-admin-network:before { content: "\f112" !important; }
+.dashicons-admin-generic:before { content: "\f111" !important; }
+.dashicons-admin-home:before { content: "\f102" !important; }
+.dashicons-admin-collapse:before { content: "\f148" !important; }
+.dashicons-filter:before { content: "\f536" !important; }
+.dashicons-admin-customizer:before { content: "\f540" !important; }
+.dashicons-admin-multisite:before { content: "\f541" !important; }
+.dashicons-admin-links:before, .dashicons-format-links:before { content: "\f103" !important; }
+.dashicons-admin-post:before, .dashicons-format-standard:before { content: "\f109" !important; }
+.dashicons-format-image:before { content: "\f128" !important; }
+.dashicons-format-gallery:before { content: "\f161" !important; }
+.dashicons-format-audio:before { content: "\f127" !important; }
+.dashicons-format-video:before { content: "\f126" !important; }
+.dashicons-format-chat:before { content: "\f125" !important; }
+.dashicons-format-status:before { content: "\f130" !important; }
+.dashicons-format-aside:before { content: "\f123" !important; }
+.dashicons-format-quote:before { content: "\f122" !important; }
+.dashicons-welcome-write-blog:before, .dashicons-welcome-edit-page:before { content: "\f119" !important; }
+.dashicons-welcome-add-page:before { content: "\f133" !important; }
+.dashicons-welcome-view-site:before { content: "\f115" !important; }
+.dashicons-welcome-widgets-menus:before { content: "\f116" !important; }
+.dashicons-welcome-comments:before { content: "\f117" !important; }
+.dashicons-welcome-learn-more:before { content: "\f118" !important; }
+.dashicons-image-crop:before { content: "\f165" !important; }
+.dashicons-image-rotate:before { content: "\f531" !important; }
+.dashicons-image-rotate-left:before { content: "\f166" !important; }
+.dashicons-image-rotate-right:before { content: "\f167" !important; }
+.dashicons-image-flip-vertical:before { content: "\f168" !important; }
+.dashicons-image-flip-horizontal:before { content: "\f169" !important; }
+.dashicons-image-filter:before { content: "\f533" !important; }
+.dashicons-undo:before { content: "\f171" !important; }
+.dashicons-redo:before { content: "\f172" !important; }
+.dashicons-editor-ul:before { content: "\f203" !important; }
+.dashicons-editor-ol:before { content: "\f204" !important; }
+.dashicons-editor-quote:before { content: "\f205" !important; }
+.dashicons-editor-alignleft:before { content: "\f206" !important; }
+.dashicons-editor-aligncenter:before { content: "\f207" !important; }
+.dashicons-editor-alignright:before { content: "\f208" !important; }
+.dashicons-editor-insertmore:before { content: "\f209" !important; }
+.dashicons-editor-spellcheck:before { content: "\f210" !important; }
+.dashicons-editor-distractionfree:before, .dashicons-editor-expand:before { content: "\f211" !important; }
+.dashicons-editor-contract:before { content: "\f506" !important; }
+.dashicons-editor-kitchensink:before { content: "\f212" !important; }
+.dashicons-editor-underline:before { content: "\f213" !important; }
+.dashicons-editor-justify:before { content: "\f214" !important; }
+.dashicons-editor-textcolor:before { content: "\f215" !important; }
+.dashicons-editor-paste-word:before { content: "\f216" !important; }
+.dashicons-editor-paste-text:before { content: "\f217" !important; }
+.dashicons-editor-removeformatting:before { content: "\f218" !important; }
+.dashicons-editor-video:before { content: "\f219" !important; }
+.dashicons-editor-customchar:before { content: "\f220" !important; }
+.dashicons-editor-outdent:before { content: "\f221" !important; }
+.dashicons-editor-indent:before { content: "\f222" !important; }
+.dashicons-editor-help:before { content: "\f223" !important; }
+.dashicons-editor-strikethrough:before { content: "\f224" !important; }
+.dashicons-editor-unlink:before { content: "\f225" !important; }
+.dashicons-editor-rtl:before { content: "\f320" !important; }
+.dashicons-editor-break:before { content: "\f474" !important; }
+.dashicons-editor-code:before { content: "\f475" !important; }
+.dashicons-editor-paragraph:before { content: "\f476" !important; }
+.dashicons-editor-table:before { content: "\f535" !important; }
+.dashicons-align-left:before { content: "\f135" !important; }
+.dashicons-align-right:before { content: "\f136" !important; }
+.dashicons-align-center:before { content: "\f134" !important; }
+.dashicons-align-none:before { content: "\f138" !important; }
+.dashicons-lock:before { content: "\f160" !important; }
+.dashicons-unlock:before { content: "\f528" !important; }
+.dashicons-calendar:before { content: "\f145" !important; }
+.dashicons-calendar-alt:before { content: "\f508" !important; }
+.dashicons-visibility:before { content: "\f177" !important; }
+.dashicons-hidden:before { content: "\f530" !important; }
+.dashicons-post-status:before { content: "\f173" !important; }
+.dashicons-edit:before { content: "\f464" !important; }
+.dashicons-post-trash:before, .dashicons-trash:before { content: "\f182" !important; }
+.dashicons-sticky:before { content: "\f537" !important; }
+.dashicons-external:before { content: "\f504" !important; }
+.dashicons-leftright:before { content: "\f229" !important; }
+.dashicons-sort:before { content: "\f156" !important; }
+.dashicons-randomize:before { content: "\f503" !important; }
+.dashicons-list-view:before { content: "\f163" !important; }
+.dashicons-exerpt-view:before, .dashicons-excerpt-view:before { content: "\f164" !important; }
+.dashicons-grid-view:before { content: "\f509" !important; }
+.dashicons-move:before { content: "\f545" !important; }
+.dashicons-hammer:before { content: "\f308" !important; }
+.dashicons-art:before { content: "\f309" !important; }
+.dashicons-migrate:before { content: "\f310" !important; }
+.dashicons-performance:before { content: "\f311" !important; }
+.dashicons-universal-access:before { content: "\f483" !important; }
+.dashicons-universal-access-alt:before { content: "\f507" !important; }
+.dashicons-tickets:before { content: "\f486" !important; }
+.dashicons-nametag:before { content: "\f484" !important; }
+.dashicons-clipboard:before { content: "\f481" !important; }
+.dashicons-heart:before { content: "\f487" !important; }
+.dashicons-megaphone:before { content: "\f488" !important; }
+.dashicons-schedule:before { content: "\f489" !important; }
+.dashicons-wordpress:before { content: "\f120" !important; }
+.dashicons-wordpress-alt:before { content: "\f324" !important; }
+.dashicons-pressthis:before { content: "\f157" !important; }
+.dashicons-update:before { content: "\f463" !important; }
+.dashicons-screenoptions:before { content: "\f180" !important; }
+.dashicons-cart:before { content: "\f174" !important; }
+.dashicons-feedback:before { content: "\f175" !important; }
+.dashicons-cloud:before { content: "\f176" !important; }
+.dashicons-translation:before { content: "\f326" !important; }
+.dashicons-tag:before { content: "\f323" !important; }
+.dashicons-category:before { content: "\f318" !important; }
+.dashicons-archive:before { content: "\f480" !important; }
+.dashicons-tagcloud:before { content: "\f479" !important; }
+.dashicons-text:before { content: "\f478" !important; }
+.dashicons-media-archive:before { content: "\f501" !important; }
+.dashicons-media-audio:before { content: "\f500" !important; }
+.dashicons-media-code:before { content: "\f499" !important; }
+.dashicons-media-default:before { content: "\f498" !important; }
+.dashicons-media-document:before { content: "\f497" !important; }
+.dashicons-media-interactive:before { content: "\f496" !important; }
+.dashicons-media-spreadsheet:before { content: "\f495" !important; }
+.dashicons-media-text:before { content: "\f491" !important; }
+.dashicons-media-video:before { content: "\f490" !important; }
+.dashicons-playlist-audio:before { content: "\f492" !important; }
+.dashicons-playlist-video:before { content: "\f493" !important; }
+.dashicons-controls-play:before { content: "\f522" !important; }
+.dashicons-controls-pause:before { content: "\f523" !important; }
+.dashicons-controls-forward:before { content: "\f519" !important; }
+.dashicons-controls-skipforward:before { content: "\f517" !important; }
+.dashicons-controls-back:before { content: "\f518" !important; }
+.dashicons-controls-skipback:before { content: "\f516" !important; }
+.dashicons-controls-repeat:before { content: "\f515" !important; }
+.dashicons-controls-volumeon:before { content: "\f521" !important; }
+.dashicons-controls-volumeoff:before { content: "\f520" !important; }
+.dashicons-yes:before { content: "\f147" !important; }
+.dashicons-no:before { content: "\f158" !important; }
+.dashicons-no-alt:before { content: "\f335" !important; }
+.dashicons-plus:before { content: "\f132" !important; }
+.dashicons-plus-alt:before { content: "\f502" !important; }
+.dashicons-plus-alt2:before { content: "\f543" !important; }
+.dashicons-minus:before { content: "\f460" !important; }
+.dashicons-dismiss:before { content: "\f153" !important; }
+.dashicons-marker:before { content: "\f159" !important; }
+.dashicons-star-filled:before { content: "\f155" !important; }
+.dashicons-star-half:before { content: "\f459" !important; }
+.dashicons-star-empty:before { content: "\f154" !important; }
+.dashicons-flag:before { content: "\f227" !important; }
+.dashicons-info:before { content: "\f348" !important; }
+.dashicons-warning:before { content: "\f534" !important; }
+.dashicons-share:before { content: "\f237" !important; }
+.dashicons-share1:before { content: "\f237" !important; }
+.dashicons-share-alt:before { content: "\f240" !important; }
+.dashicons-share-alt2:before { content: "\f242" !important; }
+.dashicons-twitter:before { content: "\f301" !important; }
+.dashicons-rss:before { content: "\f303" !important; }
+.dashicons-email:before { content: "\f465" !important; }
+.dashicons-email-alt:before { content: "\f466" !important; }
+.dashicons-facebook:before { content: "\f304" !important; }
+.dashicons-facebook-alt:before { content: "\f305" !important; }
+.dashicons-networking:before { content: "\f325" !important; }
+.dashicons-googleplus:before { content: "\f462" !important; }
+.dashicons-location:before { content: "\f230" !important; }
+.dashicons-location-alt:before { content: "\f231" !important; }
+.dashicons-camera:before { content: "\f306" !important; }
+.dashicons-images-alt:before { content: "\f232" !important; }
+.dashicons-images-alt2:before { content: "\f233" !important; }
+.dashicons-video-alt:before { content: "\f234" !important; }
+.dashicons-video-alt2:before { content: "\f235" !important; }
+.dashicons-video-alt3:before { content: "\f236" !important; }
+.dashicons-vault:before { content: "\f178" !important; }
+.dashicons-shield:before { content: "\f332" !important; }
+.dashicons-shield-alt:before { content: "\f334" !important; }
+.dashicons-sos:before { content: "\f468" !important; }
+.dashicons-search:before { content: "\f179" !important; }
+.dashicons-slides:before { content: "\f181" !important; }
+.dashicons-analytics:before { content: "\f183" !important; }
+.dashicons-chart-pie:before { content: "\f184" !important; }
+.dashicons-chart-bar:before { content: "\f185" !important; }
+.dashicons-chart-line:before { content: "\f238" !important; }
+.dashicons-chart-area:before { content: "\f239" !important; }
+.dashicons-groups:before { content: "\f307" !important; }
+.dashicons-businessman:before { content: "\f338" !important; }
+.dashicons-id:before { content: "\f336" !important; }
+.dashicons-id-alt:before { content: "\f337" !important; }
+.dashicons-products:before { content: "\f312" !important; }
+.dashicons-awards:before { content: "\f313" !important; }
+.dashicons-forms:before { content: "\f314" !important; }
+.dashicons-testimonial:before { content: "\f473" !important; }
+.dashicons-portfolio:before { content: "\f322" !important; }
+.dashicons-book:before { content: "\f330" !important; }
+.dashicons-book-alt:before { content: "\f331" !important; }
+.dashicons-download:before { content: "\f316" !important; }
+.dashicons-upload:before { content: "\f317" !important; }
+.dashicons-backup:before { content: "\f321" !important; }
+.dashicons-clock:before { content: "\f469" !important; }
+.dashicons-lightbulb:before { content: "\f339" !important; }
+.dashicons-microphone:before { content: "\f482" !important; }
+.dashicons-desktop:before { content: "\f472" !important; }
+.dashicons-laptop:before { content: "\f547" !important; }
+.dashicons-tablet:before { content: "\f471" !important; }
+.dashicons-smartphone:before { content: "\f470" !important; }
+.dashicons-phone:before { content: "\f525" !important; }
+.dashicons-smiley:before { content: "\f328" !important; }
+.dashicons-index-card:before { content: "\f510" !important; }
+.dashicons-carrot:before { content: "\f511" !important; }
+.dashicons-building:before { content: "\f512" !important; }
+.dashicons-store:before { content: "\f513" !important; }
+.dashicons-album:before { content: "\f514" !important; }
+.dashicons-palmtree:before { content: "\f527" !important; }
+.dashicons-tickets-alt:before { content: "\f524" !important; }
+.dashicons-money:before { content: "\f526" !important; }
+.dashicons-thumbs-up:before { content: "\f529" !important; }
+.dashicons-thumbs-down:before { content: "\f542" !important; }
+.dashicons-layout:before { content: "\f538" !important; }
+.dashicons-paperclip:before { content: "\f546" !important; }
diff --git a/css/_form-validation.scss b/css/_form-validation.scss
new file mode 100644
index 0000000..da71295
--- /dev/null
+++ b/css/_form-validation.scss
@@ -0,0 +1,15 @@
+$invalidColor: #d63638; //Matches the Theme Customizer.
+
+@mixin ame-invalid-input-styles {
+ select, input {
+ &:invalid {
+ border-color: $invalidColor;
+
+ //Override the box shadow that WordPress adds on focus.
+ &:focus {
+ box-shadow: 0 0 0 1px $invalidColor;
+ }
+ }
+ }
+}
+
diff --git a/css/_indeterminate-checkbox.scss b/css/_indeterminate-checkbox.scss
new file mode 100644
index 0000000..098bb0d
--- /dev/null
+++ b/css/_indeterminate-checkbox.scss
@@ -0,0 +1,38 @@
+@mixin ame-indeterminate-checkbox($markColor: #1e8cbe) {
+ &:indeterminate:before {
+ content: '\25a0'; //Unicode black square. Another option would be BLACK LARGE SQUARE (U+2B1B).
+ color: $markColor;
+
+ //Large square.
+ //margin: -6px 0 0 -1px;
+ //font: 400 18px/1 dashicons;
+
+ //Smaller square.
+ margin: -3px 0 0 -1px;
+ font: 400 14px/1 dashicons;
+
+ //Even smaller square.
+ //margin: -2px 0 0 -1px;
+ //font: 400 13px/1 dashicons;
+
+ float: left;
+ display: inline-block;
+ vertical-align: middle;
+ width: 16px;
+ -webkit-font-smoothing: antialiased;
+ }
+
+ @media screen and (max-width: 782px) {
+ &:indeterminate:before {
+ $boxSize: 1.5625rem;
+ height: $boxSize;
+ width: $boxSize;
+ line-height: $boxSize;
+ margin: -1px;
+
+ font-size: 18px;
+ font-family: unset;
+ font-weight: normal;
+ }
+ }
+}
\ No newline at end of file
diff --git a/css/_input-group.scss b/css/_input-group.scss
new file mode 100644
index 0000000..273a9e9
--- /dev/null
+++ b/css/_input-group.scss
@@ -0,0 +1,16 @@
+.ame-input-group {
+ display: flex;
+ flex-wrap: wrap;
+
+ > :not(:first-child) {
+ margin-left: -1px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+
+ > :not(:last-child) {
+ margin-right: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+}
\ No newline at end of file
diff --git a/css/_main-tabs.scss b/css/_main-tabs.scss
new file mode 100644
index 0000000..fdec1ad
--- /dev/null
+++ b/css/_main-tabs.scss
@@ -0,0 +1,37 @@
+/***************************************
+ Tabs on the settings page
+ ***************************************/
+
+.wrap.ws-ame-too-many-tabs .ws-ame-nav-tab-list {
+ &.nav-tab-wrapper {
+ border-bottom-color: transparent;
+ }
+
+ .nav-tab {
+ border-bottom: 1px solid #c3c4c7;
+ margin-bottom: 10px;
+ margin-top: 0;
+ }
+}
+
+/* Spacing between the page heading and the tab list.
+
+Normally, this is handled by .nav-tab styles, but WordPress changes the margins at smaller screen sizes
+and the tabs end up without a left margin. Let's put that margin on the heading instead and remove it
+from the first tab. */
+
+#ws_ame_editor_heading {
+ margin-right: 0.305em;
+}
+
+.ws-ame-nav-tab-list {
+ a.nav-tab:first-of-type {
+ margin-left: 0;
+ }
+}
+
+/* When in "too many tabs" mode, there's too much space between the bottom of the tab list and the rest
+of the page. I haven't found a good way to change the margins of just the last row, so here's a partial fix. */
+.ws-ame-too-many-tabs #ws_actor_selector {
+ margin-top: 0;
+}
\ No newline at end of file
diff --git a/css/_test-access-screen.scss b/css/_test-access-screen.scss
new file mode 100644
index 0000000..6f1cc02
--- /dev/null
+++ b/css/_test-access-screen.scss
@@ -0,0 +1,131 @@
+/*********************************************
+ "Test access" dialog
+**********************************************/
+
+#ws_ame_test_access_screen {
+ display: none;
+ background: #fcfcfc;
+}
+
+#ws_ame_test_inputs {
+ //border-bottom: 1px solid #ddd;
+ padding-bottom: 16px;
+}
+
+.ws_ame_test_input {
+ display: block;
+ float: left;
+
+ width: 100%;
+ margin: 2px 0;
+ box-sizing: content-box;
+}
+
+.ws_ame_test_input_name {
+ display: block;
+ float: left;
+ width: 35%;
+ margin-right: 4%;
+
+ text-align: right;
+ padding-top: 6px;
+ line-height: 16px;
+}
+
+.ws_ame_test_input_value {
+ display: block;
+ float: right;
+ width: 60%;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+#ws_ame_test_actions {
+ float: left;
+ width: 100%;
+ margin-top: 1em;
+}
+
+#ws_ame_test_button_container {
+ width: 35%;
+ margin-right: 4%;
+ float: left;
+ text-align: right;
+}
+
+#ws_ame_test_progress {
+ display: none;
+ width: 60%;
+ float: right;
+
+ .spinner {
+ float: none;
+ vertical-align: bottom;
+ margin-left: 0;
+ margin-right: 4px;
+ }
+}
+
+#ws_ame_test_access_body {
+ width: 100%;
+ position: relative;
+
+ border: 1px solid #ddd;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+#ws_ame_test_frame_container {
+ margin-right: 250px;
+ background: white;
+
+ min-height: 500px;
+ position: relative;
+}
+
+#ws_ame_test_access_frame {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ width: 100%;
+ height: 100%;
+ min-height: 500px;
+
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+
+#ws_ame_test_access_sidebar {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+
+ width: 250px;
+ padding: 16px 24px;
+
+ background-color: #f3f3f3;
+ border-left: 1px solid #ddd;
+
+ h4:first-of-type {
+ margin-top: 0;
+ }
+}
+
+#ws_ame_test_frame_placeholder {
+ display: block;
+ padding: 16px 24px;
+}
+
+#ws_ame_test_output {
+ display: none;
+}
\ No newline at end of file
diff --git a/css/admin.css b/css/admin.css
new file mode 100644
index 0000000..a99f816
--- /dev/null
+++ b/css/admin.css
@@ -0,0 +1,129 @@
+/**
+ * Miscellaneous menu styles that can be used on all admin pages.
+ */
+
+/*
+ * Submenu separators.
+ */
+hr.ws-submenu-separator {
+ display: block;
+ margin: 2px 0;
+ padding: 0;
+ height: 0;
+
+ border-width: 1px 0 0 0;
+ border-style: solid;
+ border-color: #ccc;
+}
+
+/* Custom separator style suggested by a customer (Slavo) */
+/*
+#adminmenu .ws-submenu-separator {
+ border-bottom: none;
+ border-top: 1px dotted rgba(0,0,0,.3);
+ width: 90%;
+}
+*/
+
+/* S2Member separator style */
+/*
+#adminmenu .ws-submenu-separator {
+ display: block;
+ border: 0;
+ margin: 1px 0 1px -5px;
+ padding: 0;
+ height: 1px;
+ line-height: 1px;
+ background: #CCCCCC;
+}
+*/
+
+/* Override .wp-menu-separator styles as they don't work too well in submenus. */
+#adminmenu .wp-submenu li.ws-submenu-separator-wrap {
+ margin: 0 0 0 0;
+ padding: 0;
+ height: inherit;
+}
+
+/* No pointer/hand on separators. */
+#adminmenu li.ws-submenu-separator-wrap a {
+ cursor: default;
+}
+
+/* No colored bar/marker when hovering over a separator. */
+#adminmenu li.ws-submenu-separator-wrap a:hover,
+#adminmenu li.ws-submenu-separator-wrap a:focus {
+ box-shadow: none;
+}
+
+/* No extra margin in submenus with icons. The selector uses the URL prefix because we can't control the link class.
+ * li.ws-submenu-separator-wrap would also work, but it's added via JS so there's an undesirable delay (FOUC).
+ */
+#adminmenu .ame-has-submenu-icons ul.wp-submenu li a[href^="#submenu-separator-"] {
+ margin-left: 0;
+}
+
+/*
+ * Override third-party menu icons with image icons selected by the user.
+ *
+ * Some plugins use CSS to put their menu icon in a ::before pseudo-element, like WordPress does with Dashicons.
+ * When the user assigns a custom icon URL to the menu item (e.g. "https://example.com/icon.png"), the ::before
+ * element will still be there, and it will push the custom icon out of place. To prevent that, let's forcibly
+ * hide the ::before element (note: don't do this when the user has selected a custom Dashicon/other icon fonts!).
+ */
+#adminmenu a.ame-has-custom-image-url > .wp-menu-image::before {
+ display: none !important;
+}
+
+/*
+ * Submenu icons.
+ */
+.ame-submenu-icon {
+ display: block;
+ padding-right: 8px;
+ min-width: 20px;
+
+ /*
+ Dashicons are 20x20 by default and some of them look pretty bad at smaller sizes. Submenu item titles are 16px high
+ by default. So lets hack some negative margins to make a 20px icon fit in 16px. With the current admin UI styles
+ it looks okay - submenu items are ~28px high when including padding/margins, so there's no visual overlap.
+ */
+ height: 20px;
+ margin-top: -1px;
+ margin-bottom: -3px;
+
+ vertical-align: top;
+
+ margin-left: -28px;
+ float: left;
+
+ /* Center image-based icons. Doesn't matter for dashicons. */
+ text-align: center;
+}
+
+#adminmenu .ame-has-submenu-icons > ul.wp-submenu > li > a,
+.folded #adminmenu .ame-has-submenu-icons > ul.wp-submenu > li > a {
+ /* Push all submenus to the right to ensure that items with and without icons line up nicely. */
+ padding-left: 36px;
+}
+
+#adminmenu .ame-submenu-icon img {
+ padding-top: 2px;
+ max-width: 32px;
+
+ opacity: 0.6;
+ filter: alpha(opacity=60);
+}
+
+#adminmenu .wp-submenu li:hover .ame-submenu-icon img,
+#adminmenu .wp-submenu li.current .ame-submenu-icon img {
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+
+/*
+When the admin menu is collapsed, don't show a submenu icon in the submenu header.
+*/
+.folded #adminmenu li.wp-submenu-head .ame-submenu-icon {
+ display: none;
+}
\ No newline at end of file
diff --git a/css/force-dashicons.css b/css/force-dashicons.css
new file mode 100644
index 0000000..a2903f7
--- /dev/null
+++ b/css/force-dashicons.css
@@ -0,0 +1,463 @@
+/*
+ Forcibly set menu icons to the selected custom Dashicons.
+
+ Problem:
+ Some plugins use CSS to assign icons to their admin menu items. Users want to change the icons.
+ In many cases, simply changing the icon URL doesn't work because the plugin CSS overrides it.
+
+ Workaround:
+ Add more CSS that overrides the icon styles that were set by other plugins.
+*/
+#adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon {
+ /*
+ This file was automatically generated from /wp-includes/css/dashicons.css.
+ Last update: 2017-06-07T16:55:55+00:00
+ */ }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon > .wp-menu-image::before {
+ font-family: "dashicons", sans-serif !important;
+ font-size: 20px !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon > .wp-menu-image {
+ background-image: none !important;
+ position: static; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-menu:before {
+ content: "\f333" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-site:before {
+ content: "\f319" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-media:before {
+ content: "\f104" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-page:before {
+ content: "\f105" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-comments:before {
+ content: "\f101" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-appearance:before {
+ content: "\f100" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-plugins:before {
+ content: "\f106" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-users:before {
+ content: "\f110" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-tools:before {
+ content: "\f107" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-settings:before {
+ content: "\f108" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-network:before {
+ content: "\f112" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-generic:before {
+ content: "\f111" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-home:before {
+ content: "\f102" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-collapse:before {
+ content: "\f148" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-filter:before {
+ content: "\f536" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-customizer:before {
+ content: "\f540" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-multisite:before {
+ content: "\f541" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-links:before, #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-links:before {
+ content: "\f103" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-admin-post:before, #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-standard:before {
+ content: "\f109" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-image:before {
+ content: "\f128" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-gallery:before {
+ content: "\f161" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-audio:before {
+ content: "\f127" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-video:before {
+ content: "\f126" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-chat:before {
+ content: "\f125" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-status:before {
+ content: "\f130" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-aside:before {
+ content: "\f123" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-format-quote:before {
+ content: "\f122" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-welcome-write-blog:before, #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-welcome-edit-page:before {
+ content: "\f119" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-welcome-add-page:before {
+ content: "\f133" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-welcome-view-site:before {
+ content: "\f115" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-welcome-widgets-menus:before {
+ content: "\f116" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-welcome-comments:before {
+ content: "\f117" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-welcome-learn-more:before {
+ content: "\f118" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-image-crop:before {
+ content: "\f165" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-image-rotate:before {
+ content: "\f531" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-image-rotate-left:before {
+ content: "\f166" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-image-rotate-right:before {
+ content: "\f167" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-image-flip-vertical:before {
+ content: "\f168" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-image-flip-horizontal:before {
+ content: "\f169" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-image-filter:before {
+ content: "\f533" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-undo:before {
+ content: "\f171" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-redo:before {
+ content: "\f172" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-ul:before {
+ content: "\f203" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-ol:before {
+ content: "\f204" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-quote:before {
+ content: "\f205" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-alignleft:before {
+ content: "\f206" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-aligncenter:before {
+ content: "\f207" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-alignright:before {
+ content: "\f208" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-insertmore:before {
+ content: "\f209" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-spellcheck:before {
+ content: "\f210" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-distractionfree:before, #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-expand:before {
+ content: "\f211" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-contract:before {
+ content: "\f506" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-kitchensink:before {
+ content: "\f212" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-underline:before {
+ content: "\f213" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-justify:before {
+ content: "\f214" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-textcolor:before {
+ content: "\f215" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-paste-word:before {
+ content: "\f216" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-paste-text:before {
+ content: "\f217" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-removeformatting:before {
+ content: "\f218" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-video:before {
+ content: "\f219" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-customchar:before {
+ content: "\f220" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-outdent:before {
+ content: "\f221" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-indent:before {
+ content: "\f222" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-help:before {
+ content: "\f223" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-strikethrough:before {
+ content: "\f224" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-unlink:before {
+ content: "\f225" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-rtl:before {
+ content: "\f320" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-break:before {
+ content: "\f474" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-code:before {
+ content: "\f475" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-paragraph:before {
+ content: "\f476" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-editor-table:before {
+ content: "\f535" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-align-left:before {
+ content: "\f135" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-align-right:before {
+ content: "\f136" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-align-center:before {
+ content: "\f134" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-align-none:before {
+ content: "\f138" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-lock:before {
+ content: "\f160" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-unlock:before {
+ content: "\f528" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-calendar:before {
+ content: "\f145" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-calendar-alt:before {
+ content: "\f508" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-visibility:before {
+ content: "\f177" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-hidden:before {
+ content: "\f530" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-post-status:before {
+ content: "\f173" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-edit:before {
+ content: "\f464" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-post-trash:before, #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-trash:before {
+ content: "\f182" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-sticky:before {
+ content: "\f537" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-external:before {
+ content: "\f504" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-leftright:before {
+ content: "\f229" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-sort:before {
+ content: "\f156" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-randomize:before {
+ content: "\f503" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-list-view:before {
+ content: "\f163" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-exerpt-view:before, #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-excerpt-view:before {
+ content: "\f164" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-grid-view:before {
+ content: "\f509" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-move:before {
+ content: "\f545" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-hammer:before {
+ content: "\f308" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-art:before {
+ content: "\f309" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-migrate:before {
+ content: "\f310" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-performance:before {
+ content: "\f311" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-universal-access:before {
+ content: "\f483" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-universal-access-alt:before {
+ content: "\f507" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-tickets:before {
+ content: "\f486" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-nametag:before {
+ content: "\f484" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-clipboard:before {
+ content: "\f481" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-heart:before {
+ content: "\f487" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-megaphone:before {
+ content: "\f488" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-schedule:before {
+ content: "\f489" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-wordpress:before {
+ content: "\f120" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-wordpress-alt:before {
+ content: "\f324" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-pressthis:before {
+ content: "\f157" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-update:before {
+ content: "\f463" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-screenoptions:before {
+ content: "\f180" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-cart:before {
+ content: "\f174" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-feedback:before {
+ content: "\f175" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-cloud:before {
+ content: "\f176" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-translation:before {
+ content: "\f326" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-tag:before {
+ content: "\f323" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-category:before {
+ content: "\f318" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-archive:before {
+ content: "\f480" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-tagcloud:before {
+ content: "\f479" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-text:before {
+ content: "\f478" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-archive:before {
+ content: "\f501" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-audio:before {
+ content: "\f500" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-code:before {
+ content: "\f499" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-default:before {
+ content: "\f498" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-document:before {
+ content: "\f497" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-interactive:before {
+ content: "\f496" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-spreadsheet:before {
+ content: "\f495" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-text:before {
+ content: "\f491" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-media-video:before {
+ content: "\f490" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-playlist-audio:before {
+ content: "\f492" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-playlist-video:before {
+ content: "\f493" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-play:before {
+ content: "\f522" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-pause:before {
+ content: "\f523" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-forward:before {
+ content: "\f519" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-skipforward:before {
+ content: "\f517" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-back:before {
+ content: "\f518" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-skipback:before {
+ content: "\f516" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-repeat:before {
+ content: "\f515" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-volumeon:before {
+ content: "\f521" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-controls-volumeoff:before {
+ content: "\f520" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-yes:before {
+ content: "\f147" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-no:before {
+ content: "\f158" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-no-alt:before {
+ content: "\f335" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-plus:before {
+ content: "\f132" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-plus-alt:before {
+ content: "\f502" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-plus-alt2:before {
+ content: "\f543" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-minus:before {
+ content: "\f460" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-dismiss:before {
+ content: "\f153" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-marker:before {
+ content: "\f159" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-star-filled:before {
+ content: "\f155" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-star-half:before {
+ content: "\f459" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-star-empty:before {
+ content: "\f154" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-flag:before {
+ content: "\f227" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-info:before {
+ content: "\f348" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-warning:before {
+ content: "\f534" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-share:before {
+ content: "\f237" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-share1:before {
+ content: "\f237" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-share-alt:before {
+ content: "\f240" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-share-alt2:before {
+ content: "\f242" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-twitter:before {
+ content: "\f301" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-rss:before {
+ content: "\f303" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-email:before {
+ content: "\f465" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-email-alt:before {
+ content: "\f466" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-facebook:before {
+ content: "\f304" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-facebook-alt:before {
+ content: "\f305" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-networking:before {
+ content: "\f325" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-googleplus:before {
+ content: "\f462" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-location:before {
+ content: "\f230" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-location-alt:before {
+ content: "\f231" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-camera:before {
+ content: "\f306" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-images-alt:before {
+ content: "\f232" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-images-alt2:before {
+ content: "\f233" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-video-alt:before {
+ content: "\f234" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-video-alt2:before {
+ content: "\f235" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-video-alt3:before {
+ content: "\f236" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-vault:before {
+ content: "\f178" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-shield:before {
+ content: "\f332" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-shield-alt:before {
+ content: "\f334" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-sos:before {
+ content: "\f468" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-search:before {
+ content: "\f179" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-slides:before {
+ content: "\f181" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-analytics:before {
+ content: "\f183" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-chart-pie:before {
+ content: "\f184" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-chart-bar:before {
+ content: "\f185" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-chart-line:before {
+ content: "\f238" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-chart-area:before {
+ content: "\f239" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-groups:before {
+ content: "\f307" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-businessman:before {
+ content: "\f338" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-id:before {
+ content: "\f336" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-id-alt:before {
+ content: "\f337" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-products:before {
+ content: "\f312" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-awards:before {
+ content: "\f313" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-forms:before {
+ content: "\f314" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-testimonial:before {
+ content: "\f473" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-portfolio:before {
+ content: "\f322" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-book:before {
+ content: "\f330" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-book-alt:before {
+ content: "\f331" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-download:before {
+ content: "\f316" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-upload:before {
+ content: "\f317" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-backup:before {
+ content: "\f321" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-clock:before {
+ content: "\f469" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-lightbulb:before {
+ content: "\f339" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-microphone:before {
+ content: "\f482" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-desktop:before {
+ content: "\f472" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-laptop:before {
+ content: "\f547" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-tablet:before {
+ content: "\f471" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-smartphone:before {
+ content: "\f470" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-phone:before {
+ content: "\f525" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-smiley:before {
+ content: "\f328" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-index-card:before {
+ content: "\f510" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-carrot:before {
+ content: "\f511" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-building:before {
+ content: "\f512" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-store:before {
+ content: "\f513" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-album:before {
+ content: "\f514" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-palmtree:before {
+ content: "\f527" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-tickets-alt:before {
+ content: "\f524" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-money:before {
+ content: "\f526" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-thumbs-up:before {
+ content: "\f529" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-thumbs-down:before {
+ content: "\f542" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-layout:before {
+ content: "\f538" !important; }
+ #adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon .dashicons-paperclip:before {
+ content: "\f546" !important; }
+
+/*# sourceMappingURL=force-dashicons.css.map */
diff --git a/css/force-dashicons.css.map b/css/force-dashicons.css.map
new file mode 100644
index 0000000..845eb5e
--- /dev/null
+++ b/css/force-dashicons.css.map
@@ -0,0 +1,7 @@
+{
+"version": 3,
+"mappings": "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",
+"sources": ["force-dashicons.scss","_dashicons.scss"],
+"names": [],
+"file": "force-dashicons.css"
+}
\ No newline at end of file
diff --git a/css/force-dashicons.scss b/css/force-dashicons.scss
new file mode 100644
index 0000000..6382f12
--- /dev/null
+++ b/css/force-dashicons.scss
@@ -0,0 +1,26 @@
+/*
+ Forcibly set menu icons to the selected custom Dashicons.
+
+ Problem:
+ Some plugins use CSS to assign icons to their admin menu items. Users want to change the icons.
+ In many cases, simply changing the icon URL doesn't work because the plugin CSS overrides it.
+
+ Workaround:
+ Add more CSS that overrides the icon styles that were set by other plugins.
+*/
+
+//Artificially increase selector specificity by repeating the ID.
+#adminmenu#adminmenu#adminmenu a.ame-has-custom-dashicon {
+ & > .wp-menu-image::before {
+ font-family: "dashicons", sans-serif !important;
+ font-size: 20px !important;
+ }
+
+ //Some plugins set the icon as a background image instead of a pseudo-element.
+ & > .wp-menu-image {
+ background-image: none !important;
+ position: static;
+ }
+
+ @import '_dashicons';
+}
\ No newline at end of file
diff --git a/css/jquery.qtip.css b/css/jquery.qtip.css
new file mode 100644
index 0000000..9062f8a
--- /dev/null
+++ b/css/jquery.qtip.css
@@ -0,0 +1,617 @@
+/*
+ * qTip2 - Pretty powerful tooltips - v3.0.3
+ * http://qtip2.com
+ *
+ * Copyright (c) 2016
+ * Released under the MIT licenses
+ * http://jquery.org/license
+ *
+ * Date: Wed May 11 2016 10:31 GMT+0100+0100
+ * Plugins: tips modal viewport svg imagemap ie6
+ * Styles: core basic css3
+ */
+.qtip{
+ position: absolute;
+ left: -28000px;
+ top: -28000px;
+ display: none;
+
+ max-width: 280px;
+ min-width: 50px;
+
+ font-size: 10.5px;
+ line-height: 12px;
+
+ direction: ltr;
+
+ box-shadow: none;
+ padding: 0;
+}
+
+ .qtip-content{
+ position: relative;
+ padding: 5px 9px;
+ overflow: hidden;
+
+ text-align: left;
+ word-wrap: break-word;
+ }
+
+ .qtip-titlebar{
+ position: relative;
+ padding: 5px 35px 5px 10px;
+ overflow: hidden;
+
+ border-width: 0 0 1px;
+ font-weight: bold;
+ }
+
+ .qtip-titlebar + .qtip-content{ border-top-width: 0 !important; }
+
+ /* Default close button class */
+ .qtip-close{
+ position: absolute;
+ right: -9px; top: -9px;
+ z-index: 11; /* Overlap .qtip-tip */
+
+ cursor: pointer;
+ outline: medium none;
+
+ border: 1px solid transparent;
+ }
+
+ .qtip-titlebar .qtip-close{
+ right: 4px; top: 50%;
+ margin-top: -9px;
+ }
+
+ * html .qtip-titlebar .qtip-close{ top: 16px; } /* IE fix */
+
+ .qtip-titlebar .ui-icon,
+ .qtip-icon .ui-icon{
+ display: block;
+ text-indent: -1000em;
+ direction: ltr;
+ }
+
+ .qtip-icon, .qtip-icon .ui-icon{
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ text-decoration: none;
+ }
+
+ .qtip-icon .ui-icon{
+ width: 18px;
+ height: 14px;
+
+ line-height: 14px;
+ text-align: center;
+ text-indent: 0;
+ font: normal bold 10px/13px Tahoma,sans-serif;
+
+ color: inherit;
+ background: transparent none no-repeat -100em -100em;
+ }
+
+/* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
+.qtip-focus{}
+
+/* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
+.qtip-hover{}
+
+/* Default tooltip style */
+.qtip-default{
+ border: 1px solid #F1D031;
+
+ background-color: #FFFFA3;
+ color: #555;
+}
+
+ .qtip-default .qtip-titlebar{
+ background-color: #FFEF93;
+ }
+
+ .qtip-default .qtip-icon{
+ border-color: #CCC;
+ background: #F1F1F1;
+ color: #777;
+ }
+
+ .qtip-default .qtip-titlebar .qtip-close{
+ border-color: #AAA;
+ color: #111;
+ }
+
+
+/*! Light tooltip style */
+.qtip-light{
+ background-color: white;
+ border-color: #E2E2E2;
+ color: #454545;
+}
+
+ .qtip-light .qtip-titlebar{
+ background-color: #f1f1f1;
+ }
+
+
+/*! Dark tooltip style */
+.qtip-dark{
+ background-color: #505050;
+ border-color: #303030;
+ color: #f3f3f3;
+}
+
+ .qtip-dark .qtip-titlebar{
+ background-color: #404040;
+ }
+
+ .qtip-dark .qtip-icon{
+ border-color: #444;
+ }
+
+ .qtip-dark .qtip-titlebar .ui-state-hover{
+ border-color: #303030;
+ }
+
+
+/*! Cream tooltip style */
+.qtip-cream{
+ background-color: #FBF7AA;
+ border-color: #F9E98E;
+ color: #A27D35;
+}
+
+ .qtip-cream .qtip-titlebar{
+ background-color: #F0DE7D;
+ }
+
+ .qtip-cream .qtip-close .qtip-icon{
+ background-position: -82px 0;
+ }
+
+
+/*! Red tooltip style */
+.qtip-red{
+ background-color: #F78B83;
+ border-color: #D95252;
+ color: #912323;
+}
+
+ .qtip-red .qtip-titlebar{
+ background-color: #F06D65;
+ }
+
+ .qtip-red .qtip-close .qtip-icon{
+ background-position: -102px 0;
+ }
+
+ .qtip-red .qtip-icon{
+ border-color: #D95252;
+ }
+
+ .qtip-red .qtip-titlebar .ui-state-hover{
+ border-color: #D95252;
+ }
+
+
+/*! Green tooltip style */
+.qtip-green{
+ background-color: #CAED9E;
+ border-color: #90D93F;
+ color: #3F6219;
+}
+
+ .qtip-green .qtip-titlebar{
+ background-color: #B0DE78;
+ }
+
+ .qtip-green .qtip-close .qtip-icon{
+ background-position: -42px 0;
+ }
+
+
+/*! Blue tooltip style */
+.qtip-blue{
+ background-color: #E5F6FE;
+ border-color: #ADD9ED;
+ color: #5E99BD;
+}
+
+ .qtip-blue .qtip-titlebar{
+ background-color: #D0E9F5;
+ }
+
+ .qtip-blue .qtip-close .qtip-icon{
+ background-position: -2px 0;
+ }
+
+
+.qtip-shadow{
+ -webkit-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
+ box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
+}
+
+/* Add rounded corners to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */
+.qtip-rounded,
+.qtip-tipsy,
+.qtip-bootstrap{
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.qtip-rounded .qtip-titlebar{
+ -moz-border-radius: 4px 4px 0 0;
+ -webkit-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+/* Youtube tooltip style */
+.qtip-youtube{
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+
+ -webkit-box-shadow: 0 0 3px #333;
+ -moz-box-shadow: 0 0 3px #333;
+ box-shadow: 0 0 3px #333;
+
+ color: white;
+ border: 0 solid transparent;
+
+ background: #4A4A4A;
+ background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,black));
+ background-image: -webkit-linear-gradient(top,#4A4A4A 0,black 100%);
+ background-image: -moz-linear-gradient(top,#4A4A4A 0,black 100%);
+ background-image: -ms-linear-gradient(top,#4A4A4A 0,black 100%);
+ background-image: -o-linear-gradient(top,#4A4A4A 0,black 100%);
+}
+
+ .qtip-youtube .qtip-titlebar{
+ background-color: #4A4A4A;
+ background-color: rgba(0,0,0,0);
+ }
+
+ .qtip-youtube .qtip-content{
+ padding: .75em;
+ font: 12px arial,sans-serif;
+
+ filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);
+ -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);";
+ }
+
+ .qtip-youtube .qtip-icon{
+ border-color: #222;
+ }
+
+ .qtip-youtube .qtip-titlebar .ui-state-hover{
+ border-color: #303030;
+ }
+
+
+/* jQuery TOOLS Tooltip style */
+.qtip-jtools{
+ background: #232323;
+ background: rgba(0, 0, 0, 0.7);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#717171), to(#232323));
+ background-image: -moz-linear-gradient(top, #717171, #232323);
+ background-image: -webkit-linear-gradient(top, #717171, #232323);
+ background-image: -ms-linear-gradient(top, #717171, #232323);
+ background-image: -o-linear-gradient(top, #717171, #232323);
+
+ border: 2px solid #ddd;
+ border: 2px solid rgba(241,241,241,1);
+
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+
+ -webkit-box-shadow: 0 0 12px #333;
+ -moz-box-shadow: 0 0 12px #333;
+ box-shadow: 0 0 12px #333;
+}
+
+ /* IE Specific */
+ .qtip-jtools .qtip-titlebar{
+ background-color: transparent;
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A);
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)";
+ }
+ .qtip-jtools .qtip-content{
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323);
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)";
+ }
+
+ .qtip-jtools .qtip-titlebar,
+ .qtip-jtools .qtip-content{
+ background: transparent;
+ color: white;
+ border: 0 dashed transparent;
+ }
+
+ .qtip-jtools .qtip-icon{
+ border-color: #555;
+ }
+
+ .qtip-jtools .qtip-titlebar .ui-state-hover{
+ border-color: #333;
+ }
+
+
+/* Cluetip style */
+.qtip-cluetip{
+ -webkit-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
+ -moz-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
+ box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
+
+ background-color: #D9D9C2;
+ color: #111;
+ border: 0 dashed transparent;
+}
+
+ .qtip-cluetip .qtip-titlebar{
+ background-color: #87876A;
+ color: white;
+ border: 0 dashed transparent;
+ }
+
+ .qtip-cluetip .qtip-icon{
+ border-color: #808064;
+ }
+
+ .qtip-cluetip .qtip-titlebar .ui-state-hover{
+ border-color: #696952;
+ color: #696952;
+ }
+
+
+/* Tipsy style */
+.qtip-tipsy{
+ background: black;
+ background: rgba(0, 0, 0, .87);
+
+ color: white;
+ border: 0 solid transparent;
+
+ font-size: 11px;
+ font-family: 'Lucida Grande', sans-serif;
+ font-weight: bold;
+ line-height: 16px;
+ text-shadow: 0 1px black;
+}
+
+ .qtip-tipsy .qtip-titlebar{
+ padding: 6px 35px 0 10px;
+ background-color: transparent;
+ }
+
+ .qtip-tipsy .qtip-content{
+ padding: 6px 10px;
+ }
+
+ .qtip-tipsy .qtip-icon{
+ border-color: #222;
+ text-shadow: none;
+ }
+
+ .qtip-tipsy .qtip-titlebar .ui-state-hover{
+ border-color: #303030;
+ }
+
+
+/* Tipped style */
+.qtip-tipped{
+ border: 3px solid #959FA9;
+
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+
+ background-color: #F9F9F9;
+ color: #454545;
+
+ font-weight: normal;
+ font-family: serif;
+}
+
+ .qtip-tipped .qtip-titlebar{
+ border-bottom-width: 0;
+
+ color: white;
+ background: #3A79B8;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#3A79B8), to(#2E629D));
+ background-image: -webkit-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -moz-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -ms-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -o-linear-gradient(top, #3A79B8, #2E629D);
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)";
+ }
+
+ .qtip-tipped .qtip-icon{
+ border: 2px solid #285589;
+ background: #285589;
+ }
+
+ .qtip-tipped .qtip-icon .ui-icon{
+ background-color: #FBFBFB;
+ color: #555;
+ }
+
+
+/**
+ * Twitter Bootstrap style.
+ *
+ * Tested with IE 8, IE 9, Chrome 18, Firefox 9, Opera 11.
+ * Does not work with IE 7.
+ */
+.qtip-bootstrap{
+ /** Taken from Bootstrap body */
+ font-size: 14px;
+ line-height: 20px;
+ color: #333333;
+
+ /** Taken from Bootstrap .popover */
+ padding: 1px;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+ .qtip-bootstrap .qtip-titlebar{
+ /** Taken from Bootstrap .popover-title */
+ padding: 8px 14px;
+ margin: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 18px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ -webkit-border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ border-radius: 5px 5px 0 0;
+ }
+
+ .qtip-bootstrap .qtip-titlebar .qtip-close{
+ /**
+ * Overrides qTip2:
+ * .qtip-titlebar .qtip-close{
+ * [...]
+ * right: 4px;
+ * top: 50%;
+ * [...]
+ * border-style: solid;
+ * }
+ */
+ right: 11px;
+ top: 45%;
+ border-style: none;
+ }
+
+ .qtip-bootstrap .qtip-content{
+ /** Taken from Bootstrap .popover-content */
+ padding: 9px 14px;
+ }
+
+ .qtip-bootstrap .qtip-icon{
+ /**
+ * Overrides qTip2:
+ * .qtip-default .qtip-icon {
+ * border-color: #CCC;
+ * background: #F1F1F1;
+ * color: #777;
+ * }
+ */
+ background: transparent;
+ }
+
+ .qtip-bootstrap .qtip-icon .ui-icon{
+ /**
+ * Overrides qTip2:
+ * .qtip-icon .ui-icon{
+ * width: 18px;
+ * height: 14px;
+ * }
+ */
+ width: auto;
+ height: auto;
+
+ /* Taken from Bootstrap .close */
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 18px;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+ }
+
+ .qtip-bootstrap .qtip-icon .ui-icon:hover{
+ /* Taken from Bootstrap .close:hover */
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+ }
+
+
+/* IE9 fix - removes all filters */
+.qtip:not(.ie9haxors) div.qtip-content,
+.qtip:not(.ie9haxors) div.qtip-titlebar{
+ filter: none;
+ -ms-filter: none;
+}
+
+
+.qtip .qtip-tip{
+ margin: 0 auto;
+ overflow: hidden;
+ z-index: 10;
+
+}
+
+ /* Opera bug #357 - Incorrect tip position
+ https://github.com/Craga89/qTip2/issues/367 */
+ x:-o-prefocus, .qtip .qtip-tip{
+ visibility: hidden;
+ }
+
+ .qtip .qtip-tip,
+ .qtip .qtip-tip .qtip-vml,
+ .qtip .qtip-tip canvas{
+ position: absolute;
+
+ color: #123456;
+ background: transparent;
+ border: 0 dashed transparent;
+ }
+
+ .qtip .qtip-tip canvas{ top: 0; left: 0; }
+
+ .qtip .qtip-tip .qtip-vml{
+ behavior: url(#default#VML);
+ display: inline-block;
+ visibility: visible;
+ }
+
+
+#qtip-overlay{
+ position: fixed;
+ left: 0; top: 0;
+ width: 100%; height: 100%;
+}
+
+ /* Applied to modals with show.modal.blur set to true */
+ #qtip-overlay.blurs{ cursor: pointer; }
+
+ /* Change opacity of overlay here */
+ #qtip-overlay div{
+ position: absolute;
+ left: 0; top: 0;
+ width: 100%; height: 100%;
+
+ background-color: black;
+
+ opacity: 0.7;
+ filter:alpha(opacity=70);
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
+ }
+
+
+.qtipmodal-ie6fix{
+ position: absolute !important;
+}
diff --git a/css/jquery.qtip.min.css b/css/jquery.qtip.min.css
new file mode 100644
index 0000000..5adc4b7
--- /dev/null
+++ b/css/jquery.qtip.min.css
@@ -0,0 +1 @@
+#qtip-overlay.blurs,.qtip-close{cursor:pointer}.qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;direction:ltr;box-shadow:none;padding:0}.qtip-content,.qtip-titlebar{position:relative;overflow:hidden}.qtip-content{padding:5px 9px;text-align:left;word-wrap:break-word}.qtip-titlebar{padding:5px 35px 5px 10px;border-width:0 0 1px;font-weight:700}.qtip-titlebar+.qtip-content{border-top-width:0!important}.qtip-close{position:absolute;right:-9px;top:-9px;z-index:11;outline:0;border:1px solid transparent}.qtip-titlebar .qtip-close{right:4px;top:50%;margin-top:-9px}* html .qtip-titlebar .qtip-close{top:16px}.qtip-icon .ui-icon,.qtip-titlebar .ui-icon{display:block;text-indent:-1000em;direction:ltr}.qtip-icon,.qtip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.qtip-icon .ui-icon{width:18px;height:14px;line-height:14px;text-align:center;text-indent:0;font:normal 700 10px/13px Tahoma,sans-serif;color:inherit;background:-100em -100em no-repeat}.qtip-default{border:1px solid #F1D031;background-color:#FFFFA3;color:#555}.qtip-default .qtip-titlebar{background-color:#FFEF93}.qtip-default .qtip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.qtip-default .qtip-titlebar .qtip-close{border-color:#AAA;color:#111}.qtip-light{background-color:#fff;border-color:#E2E2E2;color:#454545}.qtip-light .qtip-titlebar{background-color:#f1f1f1}.qtip-dark{background-color:#505050;border-color:#303030;color:#f3f3f3}.qtip-dark .qtip-titlebar{background-color:#404040}.qtip-dark .qtip-icon{border-color:#444}.qtip-dark .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-cream{background-color:#FBF7AA;border-color:#F9E98E;color:#A27D35}.qtip-red,.qtip-red .qtip-icon,.qtip-red .qtip-titlebar .ui-state-hover{border-color:#D95252}.qtip-cream .qtip-titlebar{background-color:#F0DE7D}.qtip-cream .qtip-close .qtip-icon{background-position:-82px 0}.qtip-red{background-color:#F78B83;color:#912323}.qtip-red .qtip-titlebar{background-color:#F06D65}.qtip-red .qtip-close .qtip-icon{background-position:-102px 0}.qtip-green{background-color:#CAED9E;border-color:#90D93F;color:#3F6219}.qtip-green .qtip-titlebar{background-color:#B0DE78}.qtip-green .qtip-close .qtip-icon{background-position:-42px 0}.qtip-blue{background-color:#E5F6FE;border-color:#ADD9ED;color:#5E99BD}.qtip-blue .qtip-titlebar{background-color:#D0E9F5}.qtip-blue .qtip-close .qtip-icon{background-position:-2px 0}.qtip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,.15)}.qtip-bootstrap,.qtip-rounded,.qtip-tipsy{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.qtip-rounded .qtip-titlebar{-moz-border-radius:4px 4px 0 0;-webkit-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.qtip-youtube{-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;color:#fff;border:0 solid transparent;background:#4A4A4A;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,#000));background-image:-webkit-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-moz-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-ms-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-o-linear-gradient(top,#4A4A4A 0,#000 100%)}.qtip-youtube .qtip-titlebar{background-color:#4A4A4A;background-color:rgba(0,0,0,0)}.qtip-youtube .qtip-content{padding:.75em;font:12px arial,sans-serif;filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#4a4a4a, EndColorStr=#000000);-ms-filter:"progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);"}.qtip-youtube .qtip-icon{border-color:#222}.qtip-youtube .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-jtools{background:#232323;background:rgba(0,0,0,.7);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-linear-gradient(top,#717171,#232323);background-image:-ms-linear-gradient(top,#717171,#232323);background-image:-o-linear-gradient(top,#717171,#232323);border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333}.qtip-jtools .qtip-titlebar{background-color:transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171, endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)"}.qtip-jtools .qtip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A, endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)"}.qtip-jtools .qtip-content,.qtip-jtools .qtip-titlebar{background:0 0;color:#fff;border:0 dashed transparent}.qtip-jtools .qtip-icon{border-color:#555}.qtip-jtools .qtip-titlebar .ui-state-hover{border-color:#333}.qtip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,.4);box-shadow:4px 4px 5px rgba(0,0,0,.4);background-color:#D9D9C2;color:#111;border:0 dashed transparent}.qtip-cluetip .qtip-titlebar{background-color:#87876A;color:#fff;border:0 dashed transparent}.qtip-cluetip .qtip-icon{border-color:#808064}.qtip-cluetip .qtip-titlebar .ui-state-hover{border-color:#696952;color:#696952}.qtip-tipsy{background:#000;background:rgba(0,0,0,.87);color:#fff;border:0 solid transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:700;line-height:16px;text-shadow:0 1px #000}.qtip-tipsy .qtip-titlebar{padding:6px 35px 0 10px;background-color:transparent}.qtip-tipsy .qtip-content{padding:6px 10px}.qtip-tipsy .qtip-icon{border-color:#222;text-shadow:none}.qtip-tipsy .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-tipped{border:3px solid #959FA9;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#F9F9F9;color:#454545;font-weight:400;font-family:serif}.qtip-tipped .qtip-titlebar{border-bottom-width:0;color:#fff;background:#3A79B8;background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));background-image:-webkit-linear-gradient(top,#3A79B8,#2E629D);background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-ms-linear-gradient(top,#3A79B8,#2E629D);background-image:-o-linear-gradient(top,#3A79B8,#2E629D);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8, endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)"}.qtip-tipped .qtip-icon{border:2px solid #285589;background:#285589}.qtip-tipped .qtip-icon .ui-icon{background-color:#FBFBFB;color:#555}.qtip-bootstrap{font-size:14px;line-height:20px;color:#333;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.qtip-bootstrap .qtip-titlebar{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.qtip-bootstrap .qtip-titlebar .qtip-close{right:11px;top:45%;border-style:none}.qtip-bootstrap .qtip-content{padding:9px 14px}.qtip-bootstrap .qtip-icon{background:0 0}.qtip-bootstrap .qtip-icon .ui-icon{width:auto;height:auto;float:right;font-size:20px;font-weight:700;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}#qtip-overlay,#qtip-overlay div{left:0;top:0;width:100%;height:100%}.qtip-bootstrap .qtip-icon .ui-icon:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}.qtip:not(.ie9haxors) div.qtip-content,.qtip:not(.ie9haxors) div.qtip-titlebar{filter:none;-ms-filter:none}.qtip .qtip-tip{margin:0 auto;overflow:hidden;z-index:10}.qtip .qtip-tip,x:-o-prefocus{visibility:hidden}.qtip .qtip-tip,.qtip .qtip-tip .qtip-vml,.qtip .qtip-tip canvas{position:absolute;color:#123456;background:0 0;border:0 dashed transparent}.qtip .qtip-tip canvas{top:0;left:0}.qtip .qtip-tip .qtip-vml{behavior:url(#default#VML);display:inline-block;visibility:visible}#qtip-overlay{position:fixed}#qtip-overlay div{position:absolute;background-color:#000;opacity:.7;filter:alpha(opacity=70);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"}.qtipmodal-ie6fix{position:absolute!important}
\ No newline at end of file
diff --git a/css/menu-editor.css b/css/menu-editor.css
new file mode 100644
index 0000000..b612be6
--- /dev/null
+++ b/css/menu-editor.css
@@ -0,0 +1,2059 @@
+@charset "UTF-8";
+/* Admin Menu Editor CSS file */
+.ame-input-group {
+ display: flex;
+ flex-wrap: wrap;
+}
+.ame-input-group > :not(:first-child) {
+ margin-left: -1px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.ame-input-group > :not(:last-child) {
+ margin-right: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+#ws_menu_editor {
+ min-width: 780px;
+}
+
+.ame-is-free-version #ws_menu_editor {
+ margin-top: 9px;
+}
+
+.ws_main_container {
+ margin: 2px;
+ width: 316px;
+ float: left;
+ display: block;
+ border: 1px solid #ccd0d4;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
+ background-color: #FFFFFF;
+ border-radius: 0px;
+ -moz-border-radius: 0px;
+ -webkit-border-radius: 0px;
+}
+
+.ws_box {
+ min-height: 30px;
+ width: 100%;
+ margin: 0;
+}
+
+.ws_basic_container {
+ float: left;
+ display: block;
+}
+
+.ws_dropzone {
+ display: block;
+ box-sizing: border-box;
+ margin: 2px 6px;
+ border: 3px none #b4b9be;
+ height: 31px;
+}
+
+.ws_dropzone_active,
+.ws_dropzone_hover,
+.ws_top_to_submenu_drop_hover .ws_dropzone {
+ border-style: dashed;
+}
+
+.ws_dropzone_hover,
+.ws_top_to_submenu_drop_hover .ws_dropzone {
+ border-width: 1px;
+}
+
+/*************************************************
+ Actor UI
+ *************************************************/
+#ws_actor_selector li:after {
+ content: "| ";
+}
+
+#ws_actor_selector li:last-child:after {
+ content: "";
+}
+
+#ws_actor_selector li a {
+ display: inline-block;
+ text-align: center;
+}
+#ws_actor_selector li a::before {
+ display: block;
+ content: attr(data-text);
+ font-weight: bold;
+ height: 1px;
+ overflow: hidden;
+ visibility: hidden;
+ margin-bottom: -1px;
+}
+
+#ws_actor_selector {
+ margin-top: 6px;
+}
+
+/**
+ * The checkbox that lets the user show/hide a menu for the currently selected actor.
+ */
+#ws_menu_editor .ws_actor_access_checkbox,
+#ws_menu_editor input[type=checkbox].ws_actor_access_checkbox {
+ margin-right: 2px;
+ margin-left: 2px;
+ margin-top: 1px;
+ vertical-align: text-top;
+}
+#ws_menu_editor .ws_actor_access_checkbox:indeterminate:before,
+#ws_menu_editor input[type=checkbox].ws_actor_access_checkbox:indeterminate:before {
+ content: "■";
+ color: #1e8cbe;
+ margin: -3px 0 0 -1px;
+ font: 400 14px/1 dashicons;
+ float: left;
+ display: inline-block;
+ vertical-align: middle;
+ width: 16px;
+ -webkit-font-smoothing: antialiased;
+}
+@media screen and (max-width: 782px) {
+ #ws_menu_editor .ws_actor_access_checkbox:indeterminate:before,
+#ws_menu_editor input[type=checkbox].ws_actor_access_checkbox:indeterminate:before {
+ height: 1.5625rem;
+ width: 1.5625rem;
+ line-height: 1.5625rem;
+ margin: -1px;
+ font-size: 18px;
+ font-family: unset;
+ font-weight: normal;
+ }
+}
+
+@media screen and (max-width: 782px) {
+ #ws_menu_editor input[type=checkbox].ws_actor_access_checkbox:indeterminate:before {
+ margin: -6px 0 0 1px;
+ font: 400 26px/1 dashicons;
+ }
+}
+/* The checkbox is only visible when viewing the menu configuration for a specific actor. */
+#ws_menu_editor .ws_actor_access_checkbox {
+ display: none;
+}
+
+#ws_menu_editor.ws_is_actor_view .ws_actor_access_checkbox {
+ display: inline-block;
+}
+
+/* Gray-out items inaccessible to the currently selected actor */
+.ws_is_actor_view .ws_container.ws_is_hidden_for_actor {
+ background-color: #F9F9F9;
+}
+
+.ws_is_actor_view .ws_is_hidden_for_actor .ws_item_title {
+ color: #777;
+}
+
+/*
+ * The sidebar
+ */
+#ws_editor_sidebar {
+ width: auto;
+ padding: 2px;
+}
+
+#ws_menu_editor .ws_main_button {
+ clear: both;
+ display: block;
+ margin: 4px;
+ width: 130px;
+}
+
+#ws_menu_editor #ws_save_menu {
+ margin-bottom: 20px;
+}
+
+#ws_menu_editor #ws_toggle_editor_layout {
+ display: none;
+}
+
+#ws_menu_editor .ws_sidebar_button_separator {
+ display: block;
+ height: 4px;
+ margin: 0;
+ padding: 0;
+}
+
+/*
+ * Page heading and tabs
+ */
+#ws_ame_editor_heading {
+ float: left;
+}
+
+/*
+ * Menu components and widgets
+ */
+.ws_container {
+ display: block;
+ width: 296px;
+ padding: 3px;
+ margin: 2px 0 2px 6px;
+}
+body.rtl .ws_container {
+ margin-right: 6px;
+ margin-left: 0;
+}
+
+.ws_submenu {
+ min-height: 2em;
+}
+
+.ws_item_head {
+ padding: 0;
+}
+
+.ws_item_title {
+ display: inline-block;
+ padding: 2px;
+ cursor: default;
+ font-size: 13px;
+ line-height: 18px;
+}
+
+.ws_edit_link {
+ float: right;
+ margin-right: 0;
+ cursor: pointer;
+ display: block;
+ width: 40px;
+ height: 22px;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-decoration: none;
+}
+
+.ws_menu_drop_hover {
+ background-color: #43b529 !important;
+}
+
+.ws_container.ui-sortable-helper * {
+ cursor: move !important;
+}
+
+.ws_container.ws_sortable_placeholder {
+ outline: 1px dashed #b4b9be;
+ outline-offset: -1px;
+ background: none;
+ border-color: transparent;
+}
+
+/*
+ If you ever want to apply a right-arrow style to the currently selected menu item,
+ you can do it like this. Commented out for now since it doesn't look all that great,
+ but might be useful in the future.
+*/
+/*
+.ws_container {
+ position: relative;
+}
+
+.ws_menu.ws_active::after {
+ content: "";
+ display: block;
+ z-index: 1002;
+
+ border-left: 14px solid #8EB0F1;
+ border-top: 15px solid rgba(255, 255, 255, 0.1);
+ border-bottom: 15px solid rgba(255, 255, 255, 0.1);
+ background: transparent;
+
+ position: absolute;
+ right: -14px;
+ top: -1px;
+
+ width: 0;
+ height: 0;
+}
+*/
+/*
+ * A left-arrow style alternative. This one is image-based and doesn't suffer from the finicky sizing issues
+ * of CSS triangles.
+ */
+.ws_container {
+ position: relative;
+}
+
+.ws_menu.ws_active::after {
+ content: "";
+ display: block;
+ position: absolute;
+ right: -19px;
+ top: -1px;
+ width: 19px;
+ height: 30px;
+ background: transparent url("../images/submenu-tip.png") no-repeat center;
+}
+
+.ws_container.ws_menu_separator.ws_active::after,
+.ws_container.ui-sortable-helper::after {
+ background-image: none;
+}
+
+/****************************************
+ Per-menu settings fields & panels
+*****************************************/
+.ws_editbox {
+ display: block;
+ padding: 4px;
+ border-radius: 2px;
+ border-top-right-radius: 0;
+ -moz-border-radius: 2px;
+ -moz-border-radius-topright: 0;
+ -webkit-border-radius: 2px;
+ -webkit-border-top-right-radius: 0;
+}
+
+.ws_edit_panel {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+.ws_edit_field {
+ margin-bottom: 6px;
+ min-height: 45px;
+}
+.ws_edit_field:after {
+ visibility: hidden;
+ display: block;
+ height: 0;
+ font-size: 0;
+ content: " ";
+ clear: both;
+}
+
+.ws_edit_field-custom {
+ margin-top: 10px;
+}
+
+.ws_edit_field.ws_no_field_caption {
+ margin-top: 10px;
+ padding-left: 1px;
+ height: 25px;
+ min-height: 25px;
+}
+
+/*
+ * Group headings
+ */
+.ws_edit_field.ws_field_group_heading {
+ height: 1px;
+ min-height: 0;
+ padding-top: 0;
+ background: #ccc;
+ margin: 8px -4px 5px;
+}
+.ws_edit_field.ws_field_group_heading span {
+ display: none;
+ font-weight: bold;
+}
+
+/* The reset-to-default button */
+.ws_reset_button {
+ display: block;
+ float: right;
+ margin-left: 4px;
+ margin-top: 2px;
+ margin-right: 6px;
+ cursor: pointer;
+ width: 16px;
+ height: 16px;
+ vertical-align: top;
+ background: url("../images/pencil_delete_gray.png") no-repeat center;
+}
+.ame-is-wp53-plus .ws_reset_button {
+ margin-top: 5px;
+}
+
+.ws_reset_button:hover {
+ background-image: url("../images/pencil_delete.png");
+}
+
+.ws_input_default input,
+.ws_input_default select,
+.ws_input_default .ws_color_scheme_display {
+ color: gray;
+}
+
+/* No reset button for fields set to the default value and fields without a default value */
+.ws_input_default .ws_reset_button,
+.ws_has_no_default .ws_reset_button {
+ visibility: hidden;
+}
+
+/* The input box in each field editor */
+#ws_menu_editor .ws_editbox input[type=text],
+#ws_menu_editor .ws_editbox select {
+ display: block;
+ float: left;
+ width: 254px;
+ height: 25px;
+ font-size: 12px;
+ line-height: 17px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+.ame-is-wp53-plus #ws_menu_editor .ws_editbox input[type=text],
+.ame-is-wp53-plus #ws_menu_editor .ws_editbox select {
+ height: 28px;
+ margin-top: 1px;
+}
+
+#ws_menu_editor .ws_edit_field label {
+ display: block;
+ float: left;
+}
+
+#ws_menu_editor .ws_edit_field-custom input[type=checkbox] {
+ margin-top: 0;
+}
+
+#ws_menu_editor input[type=text].ws_field_value {
+ min-height: 25px;
+}
+.ame-is-wp53-plus #ws_menu_editor input[type=text].ws_field_value {
+ min-height: 28px;
+}
+
+/* Dropdown button for combo-box fields */
+#ws_menu_editor .ws_dropdown_button,
+#ws_menu_access_editor .ws_dropdown_button {
+ box-sizing: border-box;
+ width: 25px;
+ height: 25px;
+ min-height: 25px;
+ margin: 1px 1px 1px 0;
+ padding: 0 1px 0 0;
+ text-align: center;
+ font-family: dashicons;
+ font-size: 16px !important;
+ line-height: 25px;
+ border-color: #dfdfdf;
+ box-shadow: none;
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.ame-is-wp53-plus #ws_menu_editor .ws_dropdown_button,
+#ws_menu_access_editor.ame-is-wp53-plus .ws_dropdown_button {
+ height: 28px;
+ border-color: #7e8993;
+ background-color: white;
+ border-left-style: none;
+ font-size: 16px !important;
+ line-height: 24px;
+ color: #555;
+}
+.ame-is-wp53-plus #ws_menu_editor .ws_dropdown_button:hover,
+#ws_menu_access_editor.ame-is-wp53-plus .ws_dropdown_button:hover {
+ color: #23282d;
+}
+
+#ws_menu_access_editor .ws_dropdown_button {
+ display: inline-block;
+ height: 27px;
+}
+
+#ws_menu_access_editor.ame-is-wp53-plus .ws_dropdown_button {
+ height: 30px;
+}
+
+#ws_menu_editor .ws_dropdown_button {
+ display: block;
+ float: left;
+}
+
+/*
+The appearance and size of combo-box fields need to be changed
+to accommodate the drop-down button.
+*/
+#ws_menu_editor .ws_has_dropdown input.ws_field_value,
+#ws_menu_access_editor input.ws_has_dropdown {
+ margin-right: 0;
+ border-right: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+#ws_menu_access_editor input.ws_has_dropdown {
+ width: 90%;
+ box-sizing: border-box;
+ height: 27px;
+ margin-top: 1px;
+}
+
+#ws_menu_access_editor.ame-is-wp53-plus input.ws_has_dropdown {
+ height: 30px;
+}
+
+#ws_menu_editor .ws_has_dropdown input.ws_field_value {
+ width: 229px;
+}
+
+/* Unlike others, this field is just a single checkbox, so it has a smaller height */
+#ws_menu_editor .ws_edit_field-custom {
+ height: 16px;
+}
+
+/*
+ * "Show/hide advanced fields"
+ */
+.ws_toggle_container {
+ text-align: right;
+ margin-right: 27px;
+}
+
+.ws_toggle_advanced_fields {
+ color: #6087CB;
+ text-decoration: none;
+ font-size: 0.85em;
+}
+
+.ws_toggle_advanced_fields:visited, .ws_toggle_advanced_fields:active {
+ color: #6087CB;
+}
+
+.ws_toggle_advanced_fields:hover {
+ color: #d54e21;
+ text-decoration: underline;
+}
+
+/************************************
+ Menu flags
+*************************************/
+.ws_flag_container {
+ float: right;
+ margin-right: 4px;
+ padding-top: 2px;
+}
+
+.ws_flag {
+ display: block;
+ float: right;
+ width: 16px;
+ height: 16px;
+ margin-left: 4px;
+ background-repeat: no-repeat;
+}
+
+/* user-created items */
+.ws_custom_flag {
+ background-image: url("../images/page-add.png");
+}
+
+/* unused items - those that are in the default menu but not in the custom one */
+.ws_unused_flag {
+ background-image: url("../images/new-menu-badge.png");
+ width: 31px;
+}
+
+/* hidden items */
+.ws_hidden_flag {
+ background-image: url("../images/page-invisible.png");
+}
+
+/* items with custom permissions for the selected actor */
+.ws_custom_actor_permissions_flag {
+ font: 16px/1 "dashicons";
+}
+
+.ws_custom_actor_permissions_flag::before {
+ /*content: "\f160";*/
+ /* padlock */
+ content: "\f110";
+ /* human silhouette */
+ color: black;
+ filter: alpha(opacity=25);
+ /*IE 5-7*/
+ opacity: 0.25;
+}
+
+/* Hidden from everyone except the current user and Super Admin. */
+.ws_hidden_from_others_flag {
+ background-image: url("../images/font-awesome/eye-slash.png");
+}
+
+/* Item visibility can't be determined because it depends on a meta capability. */
+.ws_uncertain_meta_cap_flag::before {
+ font: 16px/1 "dashicons";
+ content: "\f348";
+ color: black;
+ filter: alpha(opacity=25);
+ /*IE 5-7*/
+ opacity: 0.25;
+}
+
+/* These classes could be used to apply different styles to items depending on their flags */
+/************************************
+ Toolbars
+*************************************/
+.ws_toolbar {
+ display: block;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 100%;
+ padding: 6px 6px 0 6px;
+}
+
+.ws_button {
+ display: block;
+ margin-right: 3px;
+ margin-bottom: 4px;
+ padding: 4px;
+ float: left;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ width: 16px;
+ height: 16px;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+}
+.ws_button img {
+ vertical-align: top;
+}
+
+a.ws_button:hover {
+ background-color: #d0e0ff;
+ border-color: #9090c0;
+}
+
+.ws_button.ws_button_disabled {
+ border-color: #ccc;
+}
+
+a.ws_button.ws_button_disabled:hover {
+ background-color: white;
+ border: 1px solid #ccc;
+}
+
+.ws_button_disabled img {
+ filter: grayscale(1);
+ -webkit-filter: grayscale(1);
+ opacity: 0.65;
+}
+
+.ws_separator {
+ float: left;
+ width: 5px;
+}
+
+#ws_toggle_toolbar, .ws_toggle_toolbar_button {
+ margin-right: 0;
+}
+
+/************************************
+ Capability selector
+*************************************/
+select.ws_dropdown {
+ width: 252px;
+ height: 20em;
+ z-index: 1002;
+ position: absolute;
+ display: none;
+ font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif;
+ font-size: 12px;
+}
+
+select.ws_dropdown option {
+ font-family: "Lucida Grande", Verdana, Arial, "Bitstream Vera Sans", sans-serif;
+ font-size: 12px;
+ padding: 3px;
+}
+
+select.ws_dropdown optgroup option {
+ padding-left: 10px;
+}
+
+/************************************
+ Tabs (small)
+ ************************************
+ Tabbed navigation for dropdowns and small dialogs.
+ */
+.ws_tool_tab_nav {
+ list-style: outside none none;
+ padding: 0;
+ margin: 0 0 0 6px;
+}
+.ws_tool_tab_nav li {
+ display: inline-block;
+ border: 1px solid transparent;
+ border-bottom-width: 0;
+ padding: 3px 5px 5px;
+ line-height: 1.35em;
+ margin-bottom: 0;
+}
+.ws_tool_tab_nav li.ui-tabs-active {
+ border-color: #dfdfdf;
+ border-bottom-color: #FDFDFD;
+ background: #FDFDFD none;
+}
+.ws_tool_tab_nav a {
+ text-decoration: none;
+}
+.ws_tool_tab_nav li.ui-tabs-active a {
+ color: #32373C;
+}
+
+.ws_tool_tab {
+ border-top: 1px solid #DFDFDF;
+ margin-top: -1px;
+ background-color: #FDFDFD;
+}
+
+/************************************
+ Icon selector
+*************************************/
+#ws_icon_selector {
+ border: 1px solid silver;
+ border-radius: 3px;
+ background-color: white;
+ width: 216px;
+ padding: 4px 0 0 0;
+ position: absolute;
+}
+
+#ws_icon_selector.ws_with_more_icons {
+ width: 570px;
+}
+
+#ws_icon_selector .ws_icon_extra {
+ display: none;
+}
+
+#ws_icon_selector.ws_with_more_icons .ws_icon_extra {
+ display: inline-block;
+}
+
+#ws_icon_selector .ws_icon_option {
+ float: left;
+ height: 30px;
+ margin: 2px;
+ cursor: pointer;
+ border: 1px solid #bbb;
+ border-radius: 3px;
+ /* Gradients and colours cribbed from WP 3.5.1 button styles */
+ background: #f3f3f3;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fefefe), to(#f4f4f4));
+ background-image: -webkit-linear-gradient(top, #fefefe, #f4f4f4);
+ background-image: -moz-linear-gradient(top, #fefefe, #f4f4f4);
+ background-image: -o-linear-gradient(top, #fefefe, #f4f4f4);
+ background-image: linear-gradient(to bottom, #fefefe, #f4f4f4);
+}
+
+#ws_icon_selector .ws_icon_option:hover {
+ /* Gradients and colours cribbed from WP 3.5.1 button styles */
+ border-color: #999;
+ background: #f3f3f3;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f3f3f3));
+ background-image: -webkit-linear-gradient(top, #fff, #f3f3f3);
+ background-image: -moz-linear-gradient(top, #fff, #f3f3f3);
+ background-image: -ms-linear-gradient(top, #fff, #f3f3f3);
+ background-image: -o-linear-gradient(top, #fff, #f3f3f3);
+ background-image: linear-gradient(to bottom, #fff, #f3f3f3);
+}
+
+#ws_icon_selector .ws_icon_option.ws_selected_icon {
+ border-color: green;
+ background-color: #deffca;
+ background-image: none;
+}
+
+#ws_icon_selector .ws_icon_option .ws_icon_image {
+ float: none;
+ margin: 0;
+ padding: 0;
+}
+#ws_icon_selector .ws_icon_option .ws_icon_image:before {
+ color: #85888c;
+ display: inline-block;
+}
+
+#ws_icon_selector .ws_icon_option .ws_icon_image.dashicons {
+ width: 20px;
+ height: 20px;
+ padding: 5px;
+}
+
+#ws_icon_selector .ws_icon_option img {
+ display: inline-block;
+ margin: 0;
+ padding: 7px;
+ width: 16px;
+ height: 16px;
+}
+
+#ws_menu_editor .ws_edit_field-icon_url input.ws_field_value {
+ width: 220px;
+ margin-right: 5px;
+}
+
+/* The icon button that displays the pop-up icon selector. */
+#ws_menu_editor .ws_select_icon {
+ margin: 0;
+ padding: 0;
+ position: relative;
+ box-sizing: border-box;
+ height: 25px;
+ min-height: 25px;
+}
+.ame-is-wp53-plus #ws_menu_editor .ws_select_icon {
+ height: 28px;
+ min-height: 28px;
+ margin-top: 1px;
+}
+
+.ws_select_icon .ws_icon_image {
+ color: #85888c;
+ padding: 3px;
+}
+.ws_select_icon .ws_icon_image.dashicons {
+ padding: 3px 2px;
+}
+.ws_select_icon .ws_icon_image.dashicons:before {
+ width: 20px;
+}
+
+/* Current icon node (image version) */
+.ws_select_icon img {
+ margin: 0;
+ padding: 4px;
+ width: 16px;
+ height: 16px;
+}
+
+#ws_icon_selector .ws_tool_tab_nav {
+ display: inline-block;
+ margin-top: 2px;
+ position: relative;
+}
+#ws_icon_selector .ws_tool_tab_nav li {
+ padding: 4px 10px 11px;
+}
+#ws_icon_selector .ws_tool_tab {
+ padding: 4px 4px 2px;
+ max-height: 324px;
+ overflow-y: auto;
+}
+
+#ws_choose_icon_from_media {
+ margin: 2px;
+}
+
+/************************************
+ Embedded page selector
+*************************************/
+#ws_embedded_page_selector {
+ width: 254px;
+ padding: 6px 0 0 0;
+ border: 1px solid silver;
+ border-radius: 3px;
+ background-color: white;
+ box-sizing: border-box;
+ position: absolute;
+}
+
+.ws_page_selector_tab_nav {
+ list-style: outside none none;
+ padding: 0;
+ margin: 0 0 0 6px;
+}
+
+.ws_page_selector_tab_nav li {
+ display: inline-block;
+ border: 1px solid transparent;
+ border-bottom-width: 0;
+ padding: 3px 5px 5px;
+ line-height: 1.35em;
+ margin-bottom: 0;
+}
+
+.ws_page_selector_tab_nav a {
+ text-decoration: none;
+}
+
+.ws_page_selector_tab_nav li.ui-tabs-active {
+ border-color: #dfdfdf;
+ background-color: #FDFDFD;
+ border-bottom-color: #FDFDFD;
+}
+
+.ws_page_selector_tab_nav li.ui-tabs-active a {
+ color: #32373C;
+}
+
+.ws_page_selector_tab {
+ border-top: 1px solid #DFDFDF;
+ padding: 12px;
+ /* The same padding as post editor boxes. */
+ margin-top: -1px;
+ background-color: #FDFDFD;
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+}
+
+#ws_current_site_pages {
+ width: 100%;
+ min-height: 150px;
+ max-height: 300px;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+#ws_embedded_page_selector input {
+ box-sizing: border-box;
+ max-width: 100%;
+}
+
+#ws_custom_embedded_page_tab p:first-child {
+ margin-top: 0;
+}
+
+/*
+ Make the "Page" field look editable. It is read-only because the user can't change it directly (they have to use
+ the dropdown), but we don't want it to be greyed-out.
+*/
+#ws_menu_editor .ws_edit_field-embedded_page_id input.ws_field_value {
+ background-color: white;
+}
+
+/************************************
+ Menu color picker
+*************************************/
+#ws-ame-menu-color-settings {
+ background: white;
+ display: none;
+}
+
+#ame-menu-color-list {
+ height: 500px;
+ overflow-y: auto;
+}
+
+.ame-menu-color-column {
+ min-width: 460px;
+}
+
+.ame-menu-color-name {
+ display: inline-block;
+ vertical-align: top;
+ padding-top: 2px;
+ line-height: 1.3;
+ font-size: 14px;
+ font-weight: 600;
+ min-width: 180px;
+}
+
+.ame-color-option {
+ padding: 10px 0;
+}
+.ame-color-option .wp-picker-container {
+ display: inline-block;
+}
+
+.ame-advanced-menu-color {
+ display: none;
+}
+
+#ws-ame-apply-colors-to-all {
+ display: block;
+ float: left;
+ margin-left: 5px;
+}
+
+/* Color presets */
+#ame-color-preset-container {
+ padding: 0 8px 8px 8px;
+ margin-left: -8px;
+ margin-right: -8px;
+ margin-bottom: 4px;
+ border-bottom: 1px solid #eee;
+}
+
+#ame-menu-color-presets {
+ width: 290px;
+ margin-right: 5px;
+}
+
+#ws-ame-save-color-preset {
+ /*margin-right: 5px;*/
+}
+
+a#ws-ame-delete-color-preset {
+ color: #A00;
+ text-decoration: none;
+}
+
+a#ws-ame-delete-color-preset:hover {
+ color: #F00;
+}
+
+/* Color scheme display in the editor widget. */
+.ws_color_scheme_display {
+ display: inline-block;
+ box-sizing: border-box;
+ height: 26px;
+ width: 190px;
+ margin-right: 5px;
+ margin-left: 1px;
+ padding: 2px 4px;
+ font-size: 12px;
+ border: 1px solid #ddd;
+ background: white;
+ cursor: pointer;
+ line-height: 20px;
+}
+.ame-is-wp53-plus .ws_color_scheme_display {
+ border-color: #7e8993;
+ border-radius: 4px;
+ margin-top: 1px;
+ margin-bottom: 1px;
+ padding: 3px 8px;
+ height: 28px;
+ line-height: 20px;
+}
+
+.ws_open_color_editor {
+ width: 58px;
+}
+
+.ws_color_display_item {
+ display: inline-block;
+ width: 18px;
+ height: 18px;
+ margin-right: 4px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+}
+
+.ws_color_display_item:last-child {
+ margin-right: 0;
+}
+
+/************************************
+ Export and import
+*************************************/
+#export_dialog, #import_dialog {
+ display: none;
+}
+
+.ui-widget-overlay {
+ background-color: black;
+ position: fixed;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ opacity: 0.7;
+ -moz-opacity: 0.7;
+ filter: alpha(opacity=70);
+ width: 100%;
+ height: 100%;
+}
+
+.ui-front {
+ z-index: 10000;
+}
+
+.settings_page_menu_editor .ui-dialog {
+ background: white;
+ border: 1px solid #c0c0c0;
+ padding: 0;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+.settings_page_menu_editor .ui-dialog .ui-dialog-content {
+ padding: 8px 8px 8px 8px;
+ font-size: 1.1em;
+}
+.settings_page_menu_editor .ui-dialog .ame-scrollable-dialog-content {
+ max-height: 500px;
+ overflow-y: auto;
+ padding-top: 0.5em;
+}
+.settings_page_menu_editor .ui-dialog-titlebar {
+ display: block;
+ height: 22px;
+ margin: 0;
+ padding: 4px 4px 4px 8px;
+ background-color: #86A7E3;
+ font-size: 1em;
+ line-height: 22px;
+ -webkit-border-top-left-radius: 4px;
+ -webkit-border-top-right-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+ -moz-border-radius-topright: 4px;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ border-bottom: 1px solid #809fd9;
+}
+.settings_page_menu_editor .ui-dialog-title {
+ color: white;
+ font-weight: bold;
+}
+.settings_page_menu_editor .ui-button.ui-dialog-titlebar-close {
+ background: #86A7E3 url(../images/x.png) no-repeat center;
+ width: 22px;
+ height: 22px;
+ display: block;
+ float: right;
+ color: white;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+}
+.settings_page_menu_editor .ui-dialog-titlebar-close:hover {
+ /*background-image: url(../images/x-light.png);*/
+ background-color: #a6c2f5;
+}
+#export_dialog .ws_dialog_panel {
+ height: 50px;
+}
+
+#import_dialog .ws_dialog_panel {
+ height: 64px;
+}
+
+.ws_dialog_panel .ame-fixed-label-text {
+ display: inline-block;
+ min-width: 6em;
+}
+.ws_dialog_panel .ame-inline-select-with-input {
+ vertical-align: baseline;
+}
+.ws_dialog_panel .ame-box-side-sizes {
+ display: flex;
+ flex-wrap: wrap;
+ max-width: 800px;
+}
+.ws_dialog_panel .ame-box-side-sizes .ame-fixed-label-text {
+ min-width: 4em;
+}
+.ws_dialog_panel .ame-box-side-sizes label {
+ margin-right: 2.5em;
+}
+.ws_dialog_panel .ame-box-side-sizes input {
+ margin-bottom: 0.4em;
+}
+.ws_dialog_panel .ame-box-side-sizes input[type=number] {
+ width: 6em;
+}
+
+.ame-flexbox-break {
+ flex-basis: 100%;
+ height: 0;
+}
+
+.ws_dialog_buttons {
+ text-align: right;
+ margin-top: 20px;
+ margin-bottom: 1px;
+ clear: both;
+}
+
+.ws_dialog_buttons .button-primary {
+ display: block;
+ float: left;
+ margin-top: 0;
+}
+
+.ws_dialog_buttons .button {
+ margin-top: 0;
+}
+
+.ws_dialog_buttons.ame-vertical-button-list {
+ text-align: left;
+}
+
+.ws_dialog_buttons.ame-vertical-button-list .button-primary {
+ float: none;
+}
+
+.ws_dialog_buttons.ame-vertical-button-list .button {
+ width: 100%;
+ text-align: left;
+ margin-bottom: 10px;
+}
+
+.ws_dialog_buttons.ame-vertical-button-list .button:last-child {
+ margin-bottom: 0;
+}
+
+#import_file_selector {
+ display: block;
+ width: 286px;
+ margin: 6px auto 12px;
+}
+
+#ws_start_import {
+ min-width: 100px;
+}
+
+#import_complete_notice {
+ text-align: center;
+ font-size: large;
+ padding-top: 25px;
+}
+
+#ws_import_error_response {
+ width: 100%;
+}
+
+.ws_dont_show_again {
+ display: inline-block;
+ margin-top: 1em;
+}
+
+/************************************
+ Menu access editor
+*************************************/
+/* The launch button */
+#ws_menu_editor .ws_edit_field-access_level input.ws_field_value {
+ width: 190px;
+ margin-right: 5px;
+}
+
+.ws_launch_access_editor {
+ min-width: 40px;
+ width: 58px;
+}
+
+#ws_menu_access_editor {
+ width: 400px;
+ display: none;
+}
+
+.ws_dialog_subpanel {
+ margin-bottom: 1em;
+}
+.ws_dialog_subpanel fieldset p {
+ margin-top: 0;
+ margin-bottom: 4px;
+}
+
+.ws-ame-dialog-subheading {
+ display: block;
+ font-weight: 600;
+ font-size: 1em;
+ margin: 0 0 0.2em 0;
+}
+
+#ws_menu_access_editor .ws_column_access,
+#ws_menu_access_editor .ws_ext_action_check_column {
+ text-align: center;
+ width: 1em;
+ padding-right: 0;
+}
+
+#ws_menu_access_editor .ws_column_access input,
+#ws_menu_access_editor .ws_ext_action_check_column input {
+ margin-right: 0;
+}
+
+#ws_menu_access_editor .ws_column_role {
+ white-space: nowrap;
+}
+
+#ws_role_table_body_container {
+ /*max-height: 400px;
+ overflow: auto;*/
+ overflow: hidden;
+ margin-right: -1px;
+}
+
+.ws_role_table_body {
+ margin-top: 2px;
+ max-width: 354px;
+}
+
+.ws_has_separate_header .ws_role_table_header {
+ border-bottom: none;
+ -moz-border-radius-bottomleft: 0;
+ -moz-border-radius-bottomright: 0;
+ -webkit-border-bottom-left-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.ws_has_separate_header .ws_role_table_body {
+ border-top: none;
+ margin-top: 0;
+ -moz-border-radius-topleft: 0;
+ -moz-border-radius-topright: 0;
+ -webkit-border-top-left-radius: 0;
+ -webkit-border-top-right-radius: 0;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.ws_role_id {
+ display: none;
+}
+
+#ws_extra_capability {
+ width: 100%;
+}
+
+#ws_role_access_container {
+ position: relative;
+ max-height: 430px;
+ overflow: auto;
+}
+
+#ws_role_access_overlay {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ line-height: 100%;
+ background: white;
+ filter: alpha(opacity=60);
+ opacity: 0.6;
+ -moz-opacity: 0.6;
+}
+
+#ws_role_access_overlay_content {
+ position: absolute;
+ width: 50%;
+ left: 22%;
+ top: 30%;
+ background: white;
+ padding: 8px;
+ border: 2px solid silver;
+ border-radius: 5px;
+ color: #555;
+}
+
+#ws_menu_access_editor div.error {
+ margin-left: 0;
+ margin-right: 0;
+ margin-bottom: 5px;
+}
+
+#ws_hardcoded_role_error {
+ display: none;
+}
+
+/*--------------------------------------------*
+ The CPT/taxonomy permissions panel
+ *--------------------------------------------*/
+/*
+ * When there are CPT/taxonomy permissions available, the appearance of the role list changes a bit.
+ */
+.ws_has_extended_permissions {
+ /* The role or actor whose CPT/taxonomy permissions are currently expanded. */
+}
+.ws_has_extended_permissions .ws_role_table_body .ws_column_role {
+ cursor: pointer;
+}
+.ws_has_extended_permissions .ws_role_table_body .ws_column_selected_role_tip {
+ display: table-cell;
+}
+.ws_has_extended_permissions .ws_role_table_body tr:hover {
+ background: #EAF2FA;
+}
+.ws_has_extended_permissions .ws_role_table_body td {
+ border-top: 1px solid #f1f1f1;
+}
+.ws_has_extended_permissions .ws_role_table_body tr:first-child td {
+ border-top-width: 0;
+}
+.ws_has_extended_permissions .ws_role_table_body tr.ws_cpt_selected_role {
+ background-color: #dddddd;
+}
+.ws_has_extended_permissions .ws_role_table_body tr.ws_cpt_selected_role .ws_column_role {
+ font-weight: bold;
+}
+.ws_has_extended_permissions .ws_role_table_body tr.ws_cpt_selected_role .ws_cpt_selected_role_tip {
+ visibility: visible;
+}
+.ws_has_extended_permissions .ws_role_table_body tr.ws_cpt_selected_role td {
+ color: #222;
+}
+
+#ws_ext_permissions_container {
+ float: left;
+ width: 352px;
+ padding: 0 9px 0 0;
+}
+
+#ws_ext_permissions_container_caption {
+ padding-left: 15px;
+ max-width: 352px;
+ position: relative;
+ white-space: nowrap;
+}
+
+#ws_ext_permissions_container .ws_ext_permissions_table {
+ margin-top: 2px;
+}
+#ws_ext_permissions_container .ws_ext_permissions_table tr td:first-child {
+ padding-left: 15px;
+}
+#ws_ext_permissions_container .ws_ext_permissions_table .ws_ext_group_title {
+ padding-bottom: 0;
+ font-weight: bold;
+}
+#ws_ext_permissions_container .ws_ext_permissions_table .ws_ext_action_check_column,
+#ws_ext_permissions_container .ws_ext_permissions_table .ws_ext_action_name_column {
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+#ws_ext_permissions_container .ws_ext_permissions_table tr.ws_ext_padding_row td {
+ padding: 0 0 0 0;
+ height: 1px;
+}
+#ws_ext_permissions_container .ws_ext_permissions_table .ws_same_as_required_cap {
+ text-decoration: underline;
+}
+#ws_ext_permissions_container .ws_ext_permissions_table .ws_ext_has_custom_setting label.ws_ext_action_name::after {
+ content: " *";
+}
+
+#ws_ext_permissions_container #ws_ext_toggle_capability_names {
+ cursor: pointer;
+ position: absolute;
+ right: 0;
+ color: #0073aa;
+}
+#ws_ext_permissions_container.ws_ext_readable_names_enabled #ws_ext_toggle_capability_names {
+ color: #b4b9be;
+}
+#ws_ext_permissions_container .ws_ext_readable_name {
+ display: none;
+}
+#ws_ext_permissions_container .ws_ext_capability {
+ display: inline;
+}
+#ws_ext_permissions_container.ws_ext_readable_names_enabled .ws_ext_readable_name {
+ display: inline;
+}
+#ws_ext_permissions_container.ws_ext_readable_names_enabled .ws_ext_capability {
+ display: none;
+}
+
+#ws_ext_permissions_container #ws_taxonomy_permissions_table tr:first-child td {
+ padding-top: 8px;
+}
+
+/* The "selected role" indicator. */
+.ws_cpt_selected_role_tip {
+ display: block;
+ visibility: hidden;
+ box-sizing: border-box;
+ width: 26px;
+ height: 26px;
+ position: absolute;
+ right: 0;
+ background: white;
+ transform: translate(1px, 0) rotate(-45deg);
+ transform-origin: top right;
+}
+
+.ws_role_table_body .ws_column_selected_role_tip {
+ display: none;
+ padding: 0;
+ width: 40px;
+ height: 100%;
+ text-align: right;
+ overflow: visible;
+ position: relative;
+ cursor: pointer;
+}
+
+.ws_ame_breadcrumb_separator {
+ color: #999;
+}
+
+#ws_menu_editor .ws_ext_permissions_indicator {
+ font-size: 16px;
+ height: 16px;
+ width: 16px;
+ visibility: hidden;
+ vertical-align: bottom;
+ cursor: pointer;
+ color: #4aa100;
+}
+
+#ws_menu_editor.ws_is_actor_view .ws_ext_permissions_indicator {
+ visibility: visible;
+}
+
+/************************************
+ Visible users dialog
+*************************************/
+#ws_visible_users_dialog {
+ background: white;
+ padding: 8px;
+}
+
+#ws_user_selection_panels {
+ min-width: 710px;
+}
+#ws_user_selection_panels .ws_user_selection_panel {
+ display: block;
+ float: left;
+ position: relative;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 350px;
+ height: 400px;
+ border: 1px solid #e5e5e5;
+ margin-right: 10px;
+ padding: 10px;
+}
+#ws_user_selection_panels #ws_user_selection_target_panel {
+ margin-right: 0;
+}
+#ws_user_selection_panels #ws_available_user_query {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 100%;
+ max-height: 28px;
+}
+#ws_user_selection_panels .ws_user_list_wrapper {
+ position: absolute;
+ top: 50px;
+ left: 10px;
+ right: 10px;
+ height: 338px;
+ overflow-x: auto;
+ overflow-y: auto;
+}
+#ws_user_selection_panels .ws_user_selection_list {
+ min-height: 20px;
+ border-width: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+#ws_user_selection_panels .ws_user_selection_list .ws_user_action_column {
+ width: 20px;
+ text-align: center;
+ padding-top: 9px;
+ padding-bottom: 0;
+}
+#ws_user_selection_panels .ws_user_selection_list .ws_user_action_button {
+ cursor: pointer;
+ color: #b4b9be;
+}
+#ws_user_selection_panels .ws_user_selection_list .ws_user_username_column {
+ padding-left: 0;
+}
+#ws_user_selection_panels .ws_user_selection_list .ws_user_display_name_column {
+ white-space: nowrap;
+}
+#ws_user_selection_panels #ws_available_users tr {
+ cursor: pointer;
+}
+#ws_user_selection_panels #ws_available_users tr:hover, #ws_user_selection_panels #ws_available_users tr.ws_user_best_match {
+ background-color: #eaf2fa;
+}
+#ws_user_selection_panels #ws_available_users tr:hover .ws_user_action_button {
+ color: #7ad03a;
+}
+#ws_user_selection_panels #ws_selected_users .ws_user_action_button::before {
+ content: "\f158";
+}
+#ws_user_selection_panels #ws_selected_users .ws_user_action_button:hover {
+ color: #dd3d36;
+}
+#ws_user_selection_panels #ws_selected_users .ws_user_action_column {
+ padding-left: 6px;
+}
+#ws_user_selection_panels #ws_selected_users .ws_user_display_name_column {
+ display: none;
+}
+#ws_user_selection_panels #ws_selected_users tr.ws_user_must_be_selected .ws_user_action_button {
+ display: none;
+}
+#ws_user_selection_panels #ws_selected_users_caption {
+ font-size: 14px;
+ line-height: 1.4em;
+ padding: 7px 10px;
+ color: #555;
+ font-weight: 600;
+}
+#ws_user_selection_panels::after {
+ display: block;
+ height: 1px;
+ visibility: hidden;
+ content: " ";
+ clear: both;
+}
+
+#ws_loading_users_indicator {
+ position: absolute;
+ right: 10px;
+ bottom: 10px;
+ margin-right: 0;
+ margin-bottom: 0;
+}
+
+/************************************
+ Menu deletion error
+*************************************/
+#ws-ame-menu-deletion-error {
+ max-width: 400px;
+}
+
+/************************************
+ Tooltips and hints
+*************************************/
+.ws_tooltip_trigger, .ws_field_tooltip_trigger {
+ cursor: pointer;
+}
+
+.ws_tooltip_content_list {
+ list-style: disc;
+ margin-left: 1em;
+ margin-bottom: 0;
+}
+
+.ws_tooltip_node {
+ font-size: 13px;
+ line-height: 1.3;
+ border-radius: 3px;
+ max-width: 300px;
+}
+
+.ws_field_tooltip_trigger .dashicons {
+ font-size: 16px;
+ height: 16px;
+ vertical-align: bottom;
+}
+
+.ws_field_tooltip_trigger {
+ color: #a1a1a1;
+}
+
+#ws_plugin_settings_form .ws_tooltip_trigger .dashicons {
+ font-size: 18px;
+}
+
+.ws_ame_custom_postbox .ws_tooltip_trigger .dashicons {
+ font-size: 18px;
+ height: 18px;
+ vertical-align: bottom;
+}
+
+.ws_tooltip_trigger.ame-warning-tooltip {
+ color: orange;
+}
+
+.ws_wide_tooltip {
+ max-width: 450px;
+}
+
+.ws_hint {
+ background: #FFFFE0;
+ border: 1px solid #E6DB55;
+ margin-bottom: 0.5em;
+ border-radius: 3px;
+ position: relative;
+ padding-right: 20px;
+}
+
+.ws_hint_close {
+ border: 1px solid #E6DB55;
+ border-right: none;
+ border-top: none;
+ color: #dcc500;
+ font-weight: bold;
+ cursor: pointer;
+ width: 18px;
+ text-align: center;
+ border-radius: 3px;
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.ws_hint_close:hover {
+ background-color: #ffef4c;
+ border-color: #e0b900;
+ color: black;
+}
+
+.ws_hint_content {
+ padding: 0.4em 0 0.4em 0.4em;
+}
+
+.ws_hint_content ul {
+ list-style: disc;
+ list-style-position: inside;
+ margin-left: 0.5em;
+}
+
+.ws_ame_doc_box .hndle, .ws_ame_custom_postbox .hndle {
+ cursor: default !important;
+ border-bottom: 1px solid #ccd0d4;
+}
+.ws_ame_doc_box .handlediv, .ws_ame_custom_postbox .handlediv {
+ display: block;
+ float: right;
+}
+.ws_ame_doc_box .inside, .ws_ame_custom_postbox .inside {
+ margin-bottom: 0;
+}
+.ws_ame_doc_box ul, .ws_ame_custom_postbox ul {
+ list-style: disc outside;
+ margin-left: 1em;
+}
+.ws_ame_doc_box li > ul, .ws_ame_custom_postbox li > ul {
+ margin-top: 6px;
+}
+.ws_ame_doc_box .button-link .toggle-indicator::before, .ws_ame_custom_postbox .button-link .toggle-indicator::before {
+ margin-top: 4px;
+ width: 20px;
+ -webkit-border-radius: 50%;
+ border-radius: 50%;
+ text-indent: -1px;
+ content: "\f142";
+ display: inline-block;
+ font: normal 20px/1 dashicons;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ text-decoration: none !important;
+}
+.ws_ame_doc_box.closed .button-link .toggle-indicator::before, .ws_ame_custom_postbox.closed .button-link .toggle-indicator::before {
+ content: "\f140";
+}
+
+.ws_basic_container .ws_ame_custom_postbox {
+ margin-left: 2px;
+ margin-right: 2px;
+}
+
+.ws_ame_custom_postbox .ame-tutorial-list {
+ margin: 0;
+}
+.ws_ame_custom_postbox .ame-tutorial-list a {
+ text-decoration: none;
+ display: block;
+ padding: 4px;
+}
+.ws_ame_custom_postbox .ame-tutorial-list ul {
+ margin-left: 1em;
+}
+.ws_ame_custom_postbox .ame-tutorial-list li {
+ display: block;
+ margin: 0;
+ list-style: none;
+}
+
+/************************************
+ Copy Permissions dialog
+*************************************/
+#ws-ame-copy-permissions-dialog select {
+ min-width: 280px;
+}
+
+/*********************************************
+ Capability suggestions and preview
+**********************************************/
+#ws_capability_suggestions {
+ padding: 4px;
+ width: 350px;
+ border: 1px solid #cdd5d5;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
+ background: #fff;
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+}
+#ws_capability_suggestions #ws_previewed_caps {
+ margin-top: 0;
+ margin-bottom: 6px;
+}
+#ws_capability_suggestions td, #ws_capability_suggestions th {
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+#ws_capability_suggestions tr.ws_preview_has_access .ws_ame_role_name {
+ background-color: lightgreen;
+}
+#ws_capability_suggestions .ws_ame_suggested_capability {
+ cursor: pointer;
+}
+#ws_capability_suggestions .ws_ame_suggested_capability:hover {
+ background-color: #d0f2d0;
+}
+
+/*********************************************
+ Settings page stuff
+**********************************************/
+#ws_plugin_settings_form figure {
+ margin-left: 0;
+ margin-top: 0;
+ margin-bottom: 1em;
+}
+
+.ame-available-add-ons tr:first-of-type td {
+ margin-top: 0;
+ padding-top: 0;
+}
+.ame-available-add-ons td {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+.ame-available-add-ons .ame-add-on-heading {
+ padding-left: 0;
+}
+
+.ame-add-on-name {
+ font-weight: 600;
+}
+
+.ame-add-on-details-link::after {
+ /*content: " \f504";
+ font-family: dashicons, sans-serif;*/
+}
+
+/*********************************************
+ WordPress 5.3+ consistent styles
+**********************************************/
+.ame-is-wp53-plus .ws_edit_field input[type=button] {
+ margin-top: 1px;
+}
+
+/*********************************************
+ CSS border style selector
+**********************************************/
+.ame-css-border-styles .ame-fixed-label-text {
+ min-width: 5em;
+}
+.ame-css-border-styles .ame-border-sample-container {
+ display: inline-block;
+ vertical-align: top;
+ min-height: 28px;
+}
+.ame-css-border-styles .ame-border-sample {
+ display: inline-block;
+ width: 14em;
+ border-top: 0.3em solid #444;
+}
+
+/*********************************************
+ Miscellaneous
+**********************************************/
+#ws_sidebar_pro_ad {
+ min-width: 225px;
+ margin-top: 5px;
+ margin-left: 3px;
+ position: fixed;
+ right: 20px;
+ bottom: 40px;
+ z-index: 100;
+}
+
+.ws-ame-icon-radio-button-group > label {
+ display: inline-block;
+ padding: 8px;
+ border: 1px solid #ccd0d4;
+ border-radius: 2px;
+ margin-right: 0.5em;
+}
+
+span.description {
+ color: #666;
+ font-style: italic;
+}
+
+.wrap :target {
+ background-color: rgba(255, 230, 81, 0.7);
+}
+
+.test-wrap {
+ background-color: #444444;
+ padding: 30px;
+}
+
+.test-container {
+ width: 400px;
+ height: 200px;
+ background-color: white;
+ border: 1px solid black;
+ border-radius: 10px;
+ overflow: hidden;
+}
+
+.test-header {
+ background-color: #67d6ff;
+ padding: 6px;
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+}
+
+.test-content {
+ padding: 8px;
+}
+
+/*********************************************
+ "Test access" dialog
+**********************************************/
+#ws_ame_test_access_screen {
+ display: none;
+ background: #fcfcfc;
+}
+
+#ws_ame_test_inputs {
+ padding-bottom: 16px;
+}
+
+.ws_ame_test_input {
+ display: block;
+ float: left;
+ width: 100%;
+ margin: 2px 0;
+ box-sizing: content-box;
+}
+
+.ws_ame_test_input_name {
+ display: block;
+ float: left;
+ width: 35%;
+ margin-right: 4%;
+ text-align: right;
+ padding-top: 6px;
+ line-height: 16px;
+}
+
+.ws_ame_test_input_value {
+ display: block;
+ float: right;
+ width: 60%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+#ws_ame_test_actions {
+ float: left;
+ width: 100%;
+ margin-top: 1em;
+}
+
+#ws_ame_test_button_container {
+ width: 35%;
+ margin-right: 4%;
+ float: left;
+ text-align: right;
+}
+
+#ws_ame_test_progress {
+ display: none;
+ width: 60%;
+ float: right;
+}
+#ws_ame_test_progress .spinner {
+ float: none;
+ vertical-align: bottom;
+ margin-left: 0;
+ margin-right: 4px;
+}
+
+#ws_ame_test_access_body {
+ width: 100%;
+ position: relative;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+#ws_ame_test_frame_container {
+ margin-right: 250px;
+ background: white;
+ min-height: 500px;
+ position: relative;
+}
+
+#ws_ame_test_access_frame {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+ min-height: 500px;
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+
+#ws_ame_test_access_sidebar {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 250px;
+ padding: 16px 24px;
+ background-color: #f3f3f3;
+ border-left: 1px solid #ddd;
+}
+#ws_ame_test_access_sidebar h4:first-of-type {
+ margin-top: 0;
+}
+
+#ws_ame_test_frame_placeholder {
+ display: block;
+ padding: 16px 24px;
+}
+
+#ws_ame_test_output {
+ display: none;
+}
+
+/***************************************
+ Tabs on the settings page
+ ***************************************/
+.wrap.ws-ame-too-many-tabs .ws-ame-nav-tab-list.nav-tab-wrapper {
+ border-bottom-color: transparent;
+}
+.wrap.ws-ame-too-many-tabs .ws-ame-nav-tab-list .nav-tab {
+ border-bottom: 1px solid #c3c4c7;
+ margin-bottom: 10px;
+ margin-top: 0;
+}
+
+/* Spacing between the page heading and the tab list.
+
+Normally, this is handled by .nav-tab styles, but WordPress changes the margins at smaller screen sizes
+and the tabs end up without a left margin. Let's put that margin on the heading instead and remove it
+from the first tab. */
+#ws_ame_editor_heading {
+ margin-right: 0.305em;
+}
+
+.ws-ame-nav-tab-list a.nav-tab:first-of-type {
+ margin-left: 0;
+}
+
+/* When in "too many tabs" mode, there's too much space between the bottom of the tab list and the rest
+of the page. I haven't found a good way to change the margins of just the last row, so here's a partial fix. */
+.ws-ame-too-many-tabs #ws_actor_selector {
+ margin-top: 0;
+}
+
+/*# sourceMappingURL=menu-editor.css.map */
diff --git a/css/menu-editor.css.map b/css/menu-editor.css.map
new file mode 100644
index 0000000..1b905b9
--- /dev/null
+++ b/css/menu-editor.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["menu-editor.scss","_input-group.scss","_indeterminate-checkbox.scss","_test-access-screen.scss","_main-tabs.scss"],"names":[],"mappings":";AAAA;ACAA;EACC;EACA;;AAEA;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;ADRF;EACC;;;AAGD;EACC;;;AAQD;EACC;EACA,OAPoB;EAQpB;EACA;EAEA;EACA;EACA;EAEA,eAb2B;EAc3B,oBAd2B;EAe3B,uBAf2B;;;AAkB5B;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AASD;EACC;EACA;EAEA;EACA;EAEA;;;AAGD;AAAA;AAAA;EAGC;;;AAGD;AAAA;EAEC;;;AAGD;AAAA;AAAA;AAGA;EACI;;;AAGJ;EACI;;;AAIH;EACC;EACA;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAIF;EACC;;;AAGD;AAAA;AAAA;AAKA;AAAA;EAGI;EACA;EACA;EACA;;AElHH;AAAA;EACC;EACA,OAH4C;EAU5C;EACA;EAMA;EACA;EACA;EACA;EACA;;AAGD;EACC;AAAA;IAEC,QADU;IAEV,OAFU;IAGV,aAHU;IAIV;IAEA;IACA;IACA;;;;AFsFH;EAEE;IACC;IACA;;;AAKH;AACA;EACI;;;AAGJ;EACI;;;AAGJ;AAEA;EACI;;;AAGJ;EACI;;;AAGJ;AAAA;AAAA;AAIA;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;AAAA;AAAA;AAIA;EACC;;;AAGD;AAAA;AAAA;AAIA;EAMC;EACA,OANY;EAQZ,SAPc;EAQd;;AAEA;EACC,cATsB;EAUtB;;;AAWF;EACC;;;AAID;EACC;;;AAGD;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;;;AAMD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;AAAA;AAAA;AAAA;AAAA;AAKA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAwBA;AAAA;AAAA;AAAA;AAKA;EACC;;;AAGD;EAKC;EACA;EAGA;EACA;EACA;EAEA,OAVkB;EAWlB,QAZmB;EAanB;;;AAID;AAAA;EAEC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EAEA;EACA;EAEA;EACA;;;AAGD;EACI;EACA;EACA;;;AAGJ;EACC;EACA;;AAGA;EACC;EACA;EACA;EACA;EAEA;EACA;;;AAIF;EACC;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;AAAA;AAAA;AAGA;EAEC;EACA;EACA;EAEA;EACA;;AAEA;EACC;EACA;;;AAIF;AACA;EACC;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;;;AAIF;EACC;;;AAGD;AAAA;AAAA;EAGC;;;AAGD;AACA;AAAA;EAEC;;;AAGD;AAIA;AAAA;EAEC;EACA;EACA,OAPiB;EAQjB;EAEA;EACA;EAEA;EACA;;AAEA;AAAA;EACC,QAhBqB;EAiBrB;;;AAIF;EACC;EACA;;;AAGD;EACC;;;AAGD;EACC;;AAEA;EACC,YAlCqB;;;AAsCvB;AAGA;AAAA;EAGC;EACA,OANqB;EAOrB;EACA;EAEA;EACA;EAEA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;;;AAGD;AAAA;EAGC,QAtEsB;EAwEtB;EACA;EACA;EAEA;EACA;EACA;;AAEA;AAAA;EACC;;;AAIF;EACC;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;AAAA;AAAA;AAAA;AAIA;AAAA;EAGC;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;AACA;EACC;;;AAGD;AAAA;AAAA;AAGA;EACC;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACA;EACC;;;AAGD;AACA;EACC;EACA;;;AAGD;AACA;EACC;;;AAGD;AACA;EACC;;;AAED;AACC;AAAsB;EACtB;AAAkB;EAClB;EAEA;AAA2B;EAC3B;;;AAGD;AACA;EACC;;;AAGD;AACA;EACC;EACA;EACA;EAEA;AAA2B;EAC3B;;;AAGD;AAMA;AAAA;AAAA;AAIA;EACC;EAEA;EACA;EACA;EAEA;EACA;;;AASD;EACC;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;AAEA;EACC;;;AAIF;EACC;EACA;;;AAID;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AAID;EACC;EACA;EACA,SAJ0B;;;AAO3B;EACC;;;AAGD;AAAA;AAAA;AAAA;AAAA;AAQA;EACC;EACA;EACA;;AAEA;EACC;EAEA;EACA;EACA;EACA;EAEA;;AAGD;EACC;EACA,qBApBwB;EAqBxB;;AAGD;EACC;;AAGD;EACC;;;AAIF;EACC;EACA;EACA,kBApCyB;;;AAyC1B;AAAA;AAAA;AAMA;EACC;EACA;EACA;EAEA;EACA;EACG;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAIJ;EACC;EACA;EAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;AACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC,OAnEc;EAoEd;;;AAIF;EACI;EACA;EACA;;;AAGJ;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;AACA;EACC;EACA;EACA;EAEG;EACA;EACH;;AAEA;EACC,QA/dqB;EAgerB,YAheqB;EAierB;;;AAIF;EACC,OA9Ge;EA+Gf;;AAEA;EACC;;AAEA;EACC;;;AAKH;AACA;EACC;EACA;EACA;EACA;;;AAOA;EACC;EACA;EAMA;;AAJA;EACC;;AAMF;EACC;EACA;EACA;;;AAIF;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EAEA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;AAAe;EACf;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAAA;AAIA;EACC;;;AAID;AAAA;AAAA;AAIA;EACI;EACA;;;AAGJ;EACI;EACA;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EAEA;EACA;EACA;EAEA;;;AAGJ;EACI;;AAEH;EACC;;;AAIF;EACI;;;AAGJ;EACC;EACA;EACA;;;AAGD;AACA;EACC;EAEA;EACA;EACA;EAEA;;;AAGD;EACC;EACA;;;AAGD;AACC;;;AAGD;EACC;EACA;;;AAED;EACC;;;AAGD;AAKA;EAGI;EACH;EACG,QAJgB;EAKhB,OATc;EAWd,cAVoB;EAWvB;EACG;EAEH;EACG;EACA;EACA;EAEH;;AAEA;EACC;EACA;EAEA;EACA;EAEA;EACA;EACA;;;AAIF;EACC;;;AAGD;EACI;EACA;EACA;EAEA;EACA;EACA;;;AAGJ;EACI;;;AAGJ;AAAA;AAAA;AAIA;EACC;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEG;EACA;;;AAGJ;EACI;;;AAIH;EACC;EACA;EAEA;EAEA;EACA;EACA;;AAEA;EACC;EACA;;AAGD;EACC;EACA;EACA;;AAIF;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EAEA;EACA;EAEA;;AAGD;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;;AAGD;AACC;EACA;;AAQF;EACC;;;AAGD;EACC;;;AAIA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;;;AAGD;EACC;EACA;EACG;EACA;;;AAGJ;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI;;;AAGJ;EACC;EACA;EAEA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACI;EACA;;;AAGJ;AAAA;AAAA;AAIA;AAGA;EAEC,OAJuB;EAKvB,cAJwB;;;AAOzB;EACC;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;;AAEA;EACC;EACA;;;AAIF;EACC;EACA;EACA;EACA;;;AAGD;AAAA;EAEC;EACA;EACA;;;AAGD;AAAA;EAEC;;;AAGD;EACC;;;AAGD;AACC;AAAA;EAEA;EACA;;;AAGD;EACC;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAIA;AAAA;AAAA;AAGA;AAuBC;;AArBA;EACC;;AAGD;EACC;;AAGD;EACC;;AAIA;EACC;;AAED;EACC;;AAKF;EACC;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;;;AAKD;EACC,cAH0B;EAI1B;EACA;EACA;;;AAGD;EACC;;AAEA;EACC,cAbyB;;AAgB1B;EACC;EACA;;AAGD;AAAA;EAEC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAIA;EACC;;;AAQF;EACC;EACA;EACA;EACA;;AAGD;EACC;;AAID;EACC;;AAED;EACC;;AAKA;EACC;;AAED;EACC;;;AAQF;EACC;;;AAIF;AACA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;EAGA;EAEA;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;EACA;EAEA;EAEA;;;AAGD;EACC;;;AAID;EAGC,WAFgB;EAGhB,QAHgB;EAIhB,OAJgB;EAOhB;EAEA;EACA;EACA;;;AAGD;EACC;;;AAGD;AAAA;AAAA;AAQA;EAEC;EACA,SAFoB;;;AAKrB;EACC;;AAEA;EACC;EACA;EACA;EAEA;EACA;EACA;EAEA,OAtBwB;EAuBxB,QAtByB;EAwBzB;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EAGA,KAFc;EAGd,MA7C0B;EA8C1B,OA9C0B;EAiD1B;EAEA;EACA;;AAGD;EACC;EAGA;EACA;EACA;EACA;;AAEA;EACC;EACA;EAEA;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAGD;EACC;;AAID;EACC;;AAMD;EACC;;AAED;EACC;;AAED;EACC;;AAGD;EACC;;AAKA;EACC;;AAaH;EACC;EACA;EACA;EAEA;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;;;AAIF;EACC;EACA,OAzJ2B;EA0J3B,QA1J2B;EA4J3B;EACA;;;AAKD;AAAA;AAAA;AAIA;EACI;;;AAMJ;AAAA;AAAA;AAIA;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAID;EACC;;;AAID;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAIA;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAGD;EACC;;;AAIF;EAEC;EACA;;;AAGD;EACC;;AAEA;EACC;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;EACA;;;AAIF;AAAA;AAAA;AAGA;EACC;;;AAGD;AAAA;AAAA;AAIA;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;AAEA;EACC;EACA;;AAGD;EAEC,aAr0CyB;EAs0CzB,gBAt0CyB;;AAy0C1B;EACC;;AAGD;EACC;;AAEA;EACC;;;AAKH;AAAA;AAAA;AAIA;EACC;EACA;EACA;;;AAIA;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;;AAIF;EACC;;;AAGD;AACC;AAAA;;;AAID;AAAA;AAAA;AAIA;EACC;;;AAGD;AAAA;AAAA;AAKC;EACC;;AAGD;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;AAIF;AAAA;AAAA;AAIA;EACI;EAEH;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;;;AAGD;EACC;EAEA;;;AAGD;EACC;;;AAGD;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EAEA;EACA;EAEA;;;AAGJ;EACI;EACA;EAEA;EACA;;;AAGJ;EACI;;;AGlsEJ;AAAA;AAAA;AAIA;EACC;EACA;;;AAGD;EAEC;;;AAGD;EACC;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;;;AAIF;EACC;EACA;EAEA;EACA;EACA;EACA;;;AAGD;EACC;EACA;EAEA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;;AAEA;EACC;;;AAIF;EACC;EACA;;;AAGD;EACC;;;ACjID;AAAA;AAAA;AAKC;EACC;;AAGD;EACC;EACA;EACA;;;AAIF;;AAAA;AAAA;AAAA;AAMA;EACC;;;AAIA;EACC;;;AAIF;AAAA;AAEA;EACC","file":"menu-editor.css"}
\ No newline at end of file
diff --git a/css/menu-editor.scss b/css/menu-editor.scss
new file mode 100644
index 0000000..916da07
--- /dev/null
+++ b/css/menu-editor.scss
@@ -0,0 +1,2248 @@
+/* Admin Menu Editor CSS file */
+
+@import "boxes";
+@import "input-group";
+
+#ws_menu_editor {
+ min-width: 780px;
+}
+
+.ame-is-free-version #ws_menu_editor {
+ margin-top: 9px;
+}
+
+$mainContainerWidth: 316px;
+$mainContainerBorderWidth: 1px;
+$mainContainerBorderRadius: 0px;
+$mainContainerBorderColor: $amePostboxBorderColor; //Was #cdd5d5 before WP 5.3.
+
+.ws_main_container {
+ margin: 2px;
+ width: $mainContainerWidth;
+ float: left;
+ display:block;
+
+ border: $mainContainerBorderWidth solid $mainContainerBorderColor;
+ box-shadow: 0 1px 1px rgba(0,0,0,0.04);
+ background-color: #FFFFFF;
+
+ border-radius: $mainContainerBorderRadius;
+ -moz-border-radius: $mainContainerBorderRadius;
+ -webkit-border-radius: $mainContainerBorderRadius;
+}
+
+.ws_box {
+ min-height: 30px;
+ width: 100%;
+ margin: 0;
+}
+
+.ws_basic_container {
+ float: left;
+ display:block;
+}
+
+#ws_menu_box {
+}
+
+#ws_submenu_box {
+}
+
+.ws_dropzone {
+ display: block;
+ box-sizing: border-box;
+
+ margin: 2px 6px;
+ border: 3px none #b4b9be;
+
+ height: 31px;
+}
+
+.ws_dropzone_active,
+.ws_dropzone_hover,
+.ws_top_to_submenu_drop_hover .ws_dropzone {
+ border-style: dashed;
+}
+
+.ws_dropzone_hover,
+.ws_top_to_submenu_drop_hover .ws_dropzone {
+ border-width: 1px;
+}
+
+/*************************************************
+ Actor UI
+ *************************************************/
+#ws_actor_selector li:after {
+ content: '| ';
+}
+
+#ws_actor_selector li:last-child:after {
+ content: '';
+}
+
+#ws_actor_selector li {
+ a {
+ display: inline-block;
+ text-align: center;
+ }
+
+ a::before {
+ display: block;
+ content: attr(data-text);
+ font-weight: bold;
+
+ height: 1px;
+ overflow: hidden;
+ visibility: hidden;
+ margin-bottom: -1px;
+ }
+}
+
+#ws_actor_selector {
+ margin-top: 6px;
+}
+
+/**
+ * The checkbox that lets the user show/hide a menu for the currently selected actor.
+ */
+@import "_indeterminate-checkbox.scss";
+
+#ws_menu_editor .ws_actor_access_checkbox,
+#ws_menu_editor input[type="checkbox"].ws_actor_access_checkbox /* Ensure we override WP defaults. */
+{
+ margin-right: 2px;
+ margin-left: 2px;
+ margin-top: 1px;
+ vertical-align: text-top;
+
+ @include ame-indeterminate-checkbox;
+}
+
+@media screen and (max-width: 782px) {
+ #ws_menu_editor input[type="checkbox"].ws_actor_access_checkbox {
+ &:indeterminate:before {
+ margin: -6px 0 0 1px;
+ font: 400 26px/1 dashicons;
+ }
+ }
+}
+
+/* The checkbox is only visible when viewing the menu configuration for a specific actor. */
+#ws_menu_editor .ws_actor_access_checkbox {
+ display: none;
+}
+
+#ws_menu_editor.ws_is_actor_view .ws_actor_access_checkbox {
+ display: inline-block;
+}
+
+/* Gray-out items inaccessible to the currently selected actor */
+
+.ws_is_actor_view .ws_container.ws_is_hidden_for_actor {
+ background-color: #F9F9F9;
+}
+
+.ws_is_actor_view .ws_is_hidden_for_actor .ws_item_title {
+ color: #777;
+}
+
+/*
+ * The sidebar
+ */
+
+#ws_editor_sidebar {
+ width: auto;
+ padding: 2px;
+}
+
+#ws_menu_editor .ws_main_button {
+ clear: both;
+ display: block;
+ margin: 4px;
+ width: 130px;
+}
+
+#ws_menu_editor #ws_save_menu {
+ margin-bottom: 20px;
+}
+
+#ws_menu_editor #ws_toggle_editor_layout {
+ display: none;
+}
+
+#ws_menu_editor .ws_sidebar_button_separator {
+ display: block;
+ height: 4px;
+ margin: 0;
+ padding: 0;
+}
+
+/*
+ * Page heading and tabs
+ */
+
+#ws_ame_editor_heading {
+ float: left;
+}
+
+/*
+ * Menu components and widgets
+ */
+
+.ws_container {
+ $itemWidth: $mainContainerWidth - 20px;
+ $itemPadding: 3px;
+ $itemBorderWidth: 1px;
+ $itemHorizontalMargin: ($mainContainerWidth - $itemWidth - $itemPadding * 2 - $itemBorderWidth * 2) / 2;
+
+ display: block;
+ width: $itemWidth;
+
+ padding : $itemPadding;
+ margin: 2px 0 2px $itemHorizontalMargin;
+
+ body.rtl & {
+ margin-right: $itemHorizontalMargin;
+ margin-left: 0;
+ }
+}
+
+.ws_active { }
+
+.ws_menu { }
+.ws_item { }
+
+.ws_menu_separator { }
+
+.ws_submenu {
+ min-height: 2em;
+}
+
+
+.ws_item_head {
+ padding: 0;
+}
+
+.ws_item_title {
+ display: inline-block;
+ padding: 2px;
+ cursor: default;
+
+ font-size: 13px;
+ line-height: 18px;
+}
+
+.ws_edit_link {
+ float: right;
+ margin-right: 0;
+ cursor: pointer;
+ display:block;
+ width: 40px;
+ height: 22px;
+
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+
+ text-decoration: none;
+}
+
+.ws_edit_link_expanded { }
+
+
+.ws_menu_drop_hover {
+ background-color: #43b529 !important;
+}
+
+.ws_container.ui-sortable-helper * {
+ cursor: move !important;
+}
+
+.ws_container.ws_sortable_placeholder {
+ outline: 1px dashed #b4b9be;
+ outline-offset: -1px;
+ background: none;
+ border-color: transparent;
+}
+
+/*
+ If you ever want to apply a right-arrow style to the currently selected menu item,
+ you can do it like this. Commented out for now since it doesn't look all that great,
+ but might be useful in the future.
+*/
+/*
+.ws_container {
+ position: relative;
+}
+
+.ws_menu.ws_active::after {
+ content: "";
+ display: block;
+ z-index: 1002;
+
+ border-left: 14px solid #8EB0F1;
+ border-top: 15px solid rgba(255, 255, 255, 0.1);
+ border-bottom: 15px solid rgba(255, 255, 255, 0.1);
+ background: transparent;
+
+ position: absolute;
+ right: -14px;
+ top: -1px;
+
+ width: 0;
+ height: 0;
+}
+*/
+
+/*
+ * A left-arrow style alternative. This one is image-based and doesn't suffer from the finicky sizing issues
+ * of CSS triangles.
+ */
+
+.ws_container {
+ position: relative;
+}
+
+.ws_menu.ws_active::after {
+ //These should match the background image size.
+ $submenuTipHeight: 30px;
+ $submenuTipWidth: 19px;
+
+ content: "";
+ display: block;
+ //z-index: 1002;
+
+ position: absolute;
+ right: -$submenuTipWidth;
+ top: -1px;
+
+ width: $submenuTipWidth;
+ height: $submenuTipHeight;
+ background: transparent url("../images/submenu-tip.png") no-repeat center;
+}
+
+//No arrows for separators and items that are being dragged.
+.ws_container.ws_menu_separator.ws_active::after,
+.ws_container.ui-sortable-helper::after {
+ background-image: none;
+}
+
+/****************************************
+ Per-menu settings fields & panels
+*****************************************/
+
+.ws_editbox {
+ display: block;
+ padding: 4px;
+
+ border-radius: 2px;
+ border-top-right-radius: 0;
+
+ -moz-border-radius: 2px;
+ -moz-border-radius-topright: 0;
+
+ -webkit-border-radius: 2px;
+ -webkit-border-top-right-radius: 0;
+}
+
+.ws_edit_panel {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+.ws_edit_field {
+ margin-bottom: 6px;
+ min-height: 45px;
+
+ //Clear-fix.
+ &:after {
+ visibility: hidden;
+ display: block;
+ height: 0;
+ font-size: 0;
+
+ content: " ";
+ clear: both;
+ }
+}
+
+.ws_edit_field-custom {
+ margin-top: 10px;
+}
+
+.ws_edit_field.ws_no_field_caption {
+ margin-top: 10px;
+ padding-left: 1px;
+ height: 25px;
+ min-height: 25px;
+}
+
+/*
+ * Group headings
+ */
+.ws_edit_field.ws_field_group_heading {
+ //display: none;
+ height: 1px;
+ min-height: 0;
+ padding-top: 0;
+
+ background: #ccc;
+ margin: 8px -4px 5px;
+
+ & span {
+ display: none;
+ font-weight: bold;
+ }
+}
+
+/* The reset-to-default button */
+.ws_reset_button {
+ display: block;
+ float: right;
+
+ margin-left: 4px;
+ margin-top: 2px;
+ margin-right: 6px;
+ cursor: pointer;
+
+ width: 16px;
+ height: 16px;
+ vertical-align: top;
+
+ background: url("../images/pencil_delete_gray.png") no-repeat center;
+
+ .ame-is-wp53-plus & {
+ margin-top: 5px;
+ }
+}
+
+.ws_reset_button:hover {
+ background-image: url("../images/pencil_delete.png");
+}
+
+.ws_input_default input,
+.ws_input_default select,
+.ws_input_default .ws_color_scheme_display {
+ color: gray;
+}
+
+/* No reset button for fields set to the default value and fields without a default value */
+.ws_input_default .ws_reset_button,
+.ws_has_no_default .ws_reset_button {
+ visibility: hidden;
+}
+
+/* The input box in each field editor */
+$basicInputWidth: 254px;
+$basicInputWp53Height: 28px;
+
+#ws_menu_editor .ws_editbox input[type="text"],
+#ws_menu_editor .ws_editbox select {
+ display: block;
+ float: left;
+ width: $basicInputWidth;
+ height: 25px;
+
+ font-size: 12px;
+ line-height: 17px;
+
+ padding-top: 3px;
+ padding-bottom: 3px;
+
+ .ame-is-wp53-plus & {
+ height: $basicInputWp53Height;
+ margin-top: 1px;
+ }
+}
+
+#ws_menu_editor .ws_edit_field label {
+ display: block;
+ float: left;
+}
+
+#ws_menu_editor .ws_edit_field-custom input[type="checkbox"] {
+ margin-top: 0;
+}
+
+#ws_menu_editor input[type="text"].ws_field_value {
+ min-height: 25px;
+
+ .ame-is-wp53-plus & {
+ min-height: $basicInputWp53Height;
+ }
+}
+
+/* Dropdown button for combo-box fields */
+$dropdownButtonWidth: 25px;
+
+#ws_menu_editor .ws_dropdown_button,
+#ws_menu_access_editor .ws_dropdown_button
+{
+ box-sizing: border-box;
+ width: $dropdownButtonWidth;
+ height: 25px;
+ min-height: 25px;
+
+ margin: 1px 1px 1px 0;
+ padding: 0 1px 0 0;
+
+ text-align: center;
+
+ font-family: dashicons;
+ font-size: 16px !important;
+ line-height: 25px;
+
+ border-color: #dfdfdf;
+ box-shadow: none;
+
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.ame-is-wp53-plus #ws_menu_editor .ws_dropdown_button,
+#ws_menu_access_editor.ame-is-wp53-plus .ws_dropdown_button
+{
+ height: $basicInputWp53Height;
+
+ border-color: #7e8993;
+ background-color: white;
+ border-left-style: none;
+
+ font-size: 16px !important;
+ line-height: 24px;
+ color: #555;
+
+ &:hover {
+ color: #23282d;
+ }
+}
+
+#ws_menu_access_editor .ws_dropdown_button {
+ display: inline-block;
+ height: 27px;
+}
+
+#ws_menu_access_editor.ame-is-wp53-plus .ws_dropdown_button {
+ height: 30px;
+}
+
+#ws_menu_editor .ws_dropdown_button {
+ display: block;
+ float: left;
+}
+
+/*
+The appearance and size of combo-box fields need to be changed
+to accommodate the drop-down button.
+*/
+#ws_menu_editor .ws_has_dropdown input.ws_field_value,
+#ws_menu_access_editor input.ws_has_dropdown
+{
+ margin-right: 0;
+ border-right: 0;
+
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+#ws_menu_access_editor input.ws_has_dropdown {
+ width: 90%;
+ box-sizing: border-box;
+ height: 27px;
+ margin-top: 1px;
+}
+
+#ws_menu_access_editor.ame-is-wp53-plus input.ws_has_dropdown {
+ height: 30px;
+}
+
+#ws_menu_editor .ws_has_dropdown input.ws_field_value {
+ width: $basicInputWidth - $dropdownButtonWidth;
+}
+
+/* Unlike others, this field is just a single checkbox, so it has a smaller height */
+#ws_menu_editor .ws_edit_field-custom {
+ height: 16px;
+}
+
+/*
+ * "Show/hide advanced fields"
+ */
+.ws_toggle_container {
+ text-align: right;
+ margin-right: 27px;
+}
+
+.ws_toggle_advanced_fields {
+ color: #6087CB;
+ text-decoration: none;
+ font-size: 0.85em;
+}
+
+.ws_toggle_advanced_fields:visited, .ws_toggle_advanced_fields:active {
+ color: #6087CB;
+}
+
+.ws_toggle_advanced_fields:hover {
+ color: #d54e21;
+ text-decoration: underline;
+}
+
+/************************************
+ Menu flags
+*************************************/
+
+.ws_flag_container {
+ float: right;
+ margin-right: 4px;
+ padding-top: 2px;
+}
+
+.ws_flag {
+ display: block;
+ float: right;
+ width: 16px;
+ height: 16px;
+ margin-left: 4px;
+ background-repeat: no-repeat;
+}
+
+/* user-created items */
+.ws_custom_flag {
+ background-image: url('../images/page-add.png');
+}
+
+/* unused items - those that are in the default menu but not in the custom one */
+.ws_unused_flag {
+ background-image: url('../images/new-menu-badge.png');
+ width: 31px;
+}
+
+/* hidden items */
+.ws_hidden_flag {
+ background-image: url('../images/page-invisible.png');
+}
+
+/* items with custom permissions for the selected actor */
+.ws_custom_actor_permissions_flag {
+ font: 16px/1 'dashicons';
+}
+.ws_custom_actor_permissions_flag::before {
+ /*content: "\f160";*/ /* padlock */
+ content: "\f110"; /* human silhouette */
+ color: black;
+
+ filter: alpha(opacity=25); /*IE 5-7*/
+ opacity: 0.25;
+}
+
+/* Hidden from everyone except the current user and Super Admin. */
+.ws_hidden_from_others_flag {
+ background-image: url('../images/font-awesome/eye-slash.png');
+}
+
+/* Item visibility can't be determined because it depends on a meta capability. */
+.ws_uncertain_meta_cap_flag::before {
+ font: 16px/1 'dashicons';
+ content: "\f348";
+ color: black;
+
+ filter: alpha(opacity=25); /*IE 5-7*/
+ opacity: 0.25;
+}
+
+/* These classes could be used to apply different styles to items depending on their flags */
+.ws_custom { }
+.ws_hidden { }
+.ws_unused { }
+
+
+/************************************
+ Toolbars
+*************************************/
+
+.ws_toolbar {
+ display: block;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ width: 100%;
+ padding: 6px 6px 0 6px;
+ //height: 34px;
+}
+
+.ws_button_container {
+ //padding-left: 6px;
+ //padding-top: 6px;
+}
+
+.ws_button {
+ display: block;
+ margin-right: 3px;
+ margin-bottom: 4px;
+
+ padding: 4px;
+ float: left;
+
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+
+ width: 16px;
+ height: 16px;
+
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+
+ img {
+ vertical-align: top;
+ }
+}
+
+a.ws_button:hover {
+ background-color: #d0e0ff;
+ border-color: #9090c0;
+}
+
+//Disabled button state.
+.ws_button.ws_button_disabled {
+ border-color: #ccc;
+}
+a.ws_button.ws_button_disabled:hover {
+ background-color: white;
+ border: 1px solid #ccc;
+}
+.ws_button_disabled img {
+ filter: grayscale(1);
+ -webkit-filter: grayscale(1);
+ opacity: 0.65;
+}
+
+.ws_separator {
+ float: left;
+ width: 5px;
+}
+
+#ws_toggle_toolbar, .ws_toggle_toolbar_button {
+ margin-right: 0;
+}
+
+/************************************
+ Capability selector
+*************************************/
+
+select.ws_dropdown {
+ width: 252px;
+ height: 20em;
+
+ z-index: 1002;
+ position: absolute;
+ display: none;
+
+ font-family : "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
+ font-size: 12px;
+}
+
+$dropdownOptionPaddingTop: 3px;
+select.ws_dropdown option {
+ font-family : "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
+ font-size: 12px;
+ padding: $dropdownOptionPaddingTop;
+}
+
+select.ws_dropdown optgroup option {
+ padding-left: 10px;
+}
+
+/************************************
+ Tabs (small)
+ ************************************
+ Tabbed navigation for dropdowns and small dialogs.
+ */
+
+$activeToolTabBackground: #FDFDFD;
+
+.ws_tool_tab_nav {
+ list-style: outside none none;
+ padding: 0;
+ margin: 0 0 0 6px;
+
+ li {
+ display: inline-block;
+
+ border: 1px solid transparent;
+ border-bottom-width: 0;
+ padding: 3px 5px 5px;
+ line-height: 1.35em;
+
+ margin-bottom: 0;
+ }
+
+ li.ui-tabs-active {
+ border-color: #dfdfdf;
+ border-bottom-color: $activeToolTabBackground;
+ background: #FDFDFD none;
+ }
+
+ a {
+ text-decoration: none;
+ }
+
+ li.ui-tabs-active a {
+ color: #32373C;
+ }
+}
+
+.ws_tool_tab {
+ border-top: 1px solid #DFDFDF;
+ margin-top: -1px;
+ background-color: $activeToolTabBackground;
+
+ //Suggestion: Use 12px inner padding like in post editor boxes.
+}
+
+/************************************
+ Icon selector
+*************************************/
+
+$iconFontColor: #85888c;
+
+#ws_icon_selector {
+ border: 1px solid silver;
+ border-radius: 3px;
+ background-color: white;
+
+ width: 216px;
+ padding: 4px 0 0 0;
+ position: absolute;
+}
+
+#ws_icon_selector.ws_with_more_icons {
+ width: 570px;
+}
+
+#ws_icon_selector .ws_icon_extra {
+ display: none;
+}
+
+#ws_icon_selector.ws_with_more_icons .ws_icon_extra {
+ display: inline-block;
+}
+
+
+#ws_icon_selector .ws_icon_option {
+ float: left;
+ height: 30px;
+
+ margin: 2px;
+ cursor: pointer;
+ border: 1px solid #bbb;
+ border-radius: 3px;
+
+ /* Gradients and colours cribbed from WP 3.5.1 button styles */
+ background: #f3f3f3;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fefefe), to(#f4f4f4));
+ background-image: -webkit-linear-gradient(top, #fefefe, #f4f4f4);
+ background-image: -moz-linear-gradient(top, #fefefe, #f4f4f4);
+ background-image: -o-linear-gradient(top, #fefefe, #f4f4f4);
+ background-image: linear-gradient(to bottom, #fefefe, #f4f4f4);
+}
+
+#ws_icon_selector .ws_icon_option:hover {
+ /* Gradients and colours cribbed from WP 3.5.1 button styles */
+ border-color: #999;
+ background: #f3f3f3;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f3f3f3));
+ background-image: -webkit-linear-gradient(top, #fff, #f3f3f3);
+ background-image: -moz-linear-gradient(top, #fff, #f3f3f3);
+ background-image: -ms-linear-gradient(top, #fff, #f3f3f3);
+ background-image: -o-linear-gradient(top, #fff, #f3f3f3);
+ background-image: linear-gradient(to bottom, #fff, #f3f3f3);
+}
+
+#ws_icon_selector .ws_icon_option.ws_selected_icon {
+ border-color: green;
+ background-color: #deffca;
+ background-image: none;
+}
+
+#ws_icon_selector .ws_icon_option .ws_icon_image {
+ float: none;
+ margin: 0;
+ padding: 0;
+
+ &:before {
+ color: $iconFontColor;
+ display: inline-block;
+ }
+}
+
+#ws_icon_selector .ws_icon_option .ws_icon_image.dashicons {
+ width: 20px;
+ height: 20px;
+ padding: 5px;
+}
+
+#ws_icon_selector .ws_icon_option img {
+ display: inline-block;
+ margin: 0;
+ padding: 7px;
+
+ width: 16px;
+ height: 16px;
+}
+
+#ws_menu_editor .ws_edit_field-icon_url input.ws_field_value {
+ width: 220px;
+ margin-right: 5px;
+}
+
+/* The icon button that displays the pop-up icon selector. */
+#ws_menu_editor .ws_select_icon {
+ margin: 0;
+ padding: 0;
+ position: relative;
+
+ box-sizing: border-box;
+ height: 25px;
+ min-height: 25px;
+
+ .ame-is-wp53-plus & {
+ height: $basicInputWp53Height;
+ min-height: $basicInputWp53Height;
+ margin-top: 1px;
+ }
+}
+
+.ws_select_icon .ws_icon_image {
+ color: $iconFontColor;
+ padding: 3px;
+
+ &.dashicons {
+ padding: 3px 2px;
+
+ &:before {
+ width: 20px;
+ }
+ }
+}
+
+/* Current icon node (image version) */
+.ws_select_icon img {
+ margin: 0;
+ padding: 4px;
+ width: 16px;
+ height: 16px;
+}
+
+#ws_icon_selector {
+ $tabTopPadding: 4px;
+ $tabHorizontalPadding: 4px;
+
+ .ws_tool_tab_nav {
+ display: inline-block;
+ margin-top: 2px;
+
+ li {
+ padding: 4px 10px 11px;
+ }
+
+ position: relative;
+ }
+
+ .ws_tool_tab {
+ padding: $tabTopPadding $tabHorizontalPadding 2px;
+ max-height: 324px;
+ overflow-y: auto;
+ }
+}
+
+#ws_choose_icon_from_media {
+ margin: 2px;
+}
+
+/************************************
+ Embedded page selector
+*************************************/
+
+#ws_embedded_page_selector {
+ width: 254px;
+ padding: 6px 0 0 0;
+
+ border: 1px solid silver;
+ border-radius: 3px;
+ background-color: white;
+
+ box-sizing: border-box;
+ position: absolute;
+}
+
+.ws_page_selector_tab_nav {
+ list-style: outside none none;
+ padding: 0;
+ margin: 0 0 0 6px;
+}
+
+.ws_page_selector_tab_nav li {
+ display: inline-block;
+
+ border: 1px solid transparent;
+ border-bottom-width: 0;
+ padding: 3px 5px 5px;
+ line-height: 1.35em;
+
+ margin-bottom: 0;
+}
+
+.ws_page_selector_tab_nav a {
+ text-decoration: none;
+}
+
+.ws_page_selector_tab_nav li.ui-tabs-active {
+ border-color: #dfdfdf;
+ background-color: #FDFDFD;
+ border-bottom-color: #FDFDFD;
+}
+
+.ws_page_selector_tab_nav li.ui-tabs-active a {
+ color: #32373C;
+}
+
+.ws_page_selector_tab {
+ border-top: 1px solid #DFDFDF;
+ padding: 12px; /* The same padding as post editor boxes. */
+ margin-top: -1px;
+ background-color: #FDFDFD;
+
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+}
+
+#ws_current_site_pages {
+ width: 100%;
+ min-height: 150px;
+ max-height: 300px;
+
+ margin-left: 0;
+ margin-right: 0;
+}
+
+#ws_embedded_page_selector input {
+ box-sizing: border-box;
+ max-width: 100%;
+}
+
+#ws_custom_embedded_page_tab p:first-child {
+ margin-top: 0;
+}
+
+/*
+ Make the "Page" field look editable. It is read-only because the user can't change it directly (they have to use
+ the dropdown), but we don't want it to be greyed-out.
+*/
+#ws_menu_editor .ws_edit_field-embedded_page_id input.ws_field_value {
+ background-color: white;
+}
+
+
+/************************************
+ Menu color picker
+*************************************/
+
+#ws-ame-menu-color-settings {
+ background: white;
+ display: none;
+}
+
+#ame-menu-color-list {
+ height: 500px;
+ overflow-y: auto;
+}
+
+.ame-menu-color-column {
+ min-width: 460px;
+}
+
+.ame-menu-color-name {
+ display: inline-block;
+ vertical-align: top;
+ padding-top: 2px;
+
+ line-height: 1.3;
+ font-size: 14px;
+ font-weight: 600;
+
+ min-width: 180px;
+}
+
+.ame-color-option {
+ padding: 10px 0;
+
+ .wp-picker-container {
+ display: inline-block;
+ }
+}
+
+.ame-advanced-menu-color {
+ display: none;
+}
+
+#ws-ame-apply-colors-to-all {
+ display: block;
+ float: left;
+ margin-left: 5px;
+}
+
+/* Color presets */
+#ame-color-preset-container {
+ padding: 0 8px 8px 8px;
+
+ margin-left: -8px;
+ margin-right: -8px;
+ margin-bottom: 4px;
+
+ border-bottom: 1px solid #eee;
+}
+
+#ame-menu-color-presets {
+ width: 290px;
+ margin-right: 5px;
+}
+
+#ws-ame-save-color-preset {
+ /*margin-right: 5px;*/
+}
+
+a#ws-ame-delete-color-preset {
+ color: #A00;
+ text-decoration: none;
+}
+a#ws-ame-delete-color-preset:hover {
+ color: #F00;
+}
+
+/* Color scheme display in the editor widget. */
+
+$colorFieldWidth: 190px;
+$colorFieldRightMargin: 5px;
+
+.ws_color_scheme_display {
+ $colorFieldHeight: 26px;
+
+ display: inline-block;
+ box-sizing: border-box;
+ height: $colorFieldHeight;
+ width: $colorFieldWidth;
+
+ margin-right: $colorFieldRightMargin;
+ margin-left: 1px;
+ padding: 2px 4px;
+
+ font-size: 12px;
+ border: 1px solid #ddd;
+ background: white;
+ cursor: pointer;
+
+ line-height: $colorFieldHeight - 6px;
+
+ .ame-is-wp53-plus & {
+ border-color: #7e8993;
+ border-radius: 4px;
+
+ margin-top: 1px;
+ margin-bottom: 1px;
+
+ padding: 3px 8px;
+ height: 28px;
+ line-height: 20px;
+ }
+}
+
+.ws_open_color_editor {
+ width: $basicInputWidth - $colorFieldWidth - $colorFieldRightMargin - 1px;
+}
+
+.ws_color_display_item {
+ display: inline-block;
+ width: 18px;
+ height: 18px;
+
+ margin-right: 4px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+}
+
+.ws_color_display_item:last-child {
+ margin-right: 0;
+}
+
+/************************************
+ Export and import
+*************************************/
+
+#export_dialog, #import_dialog {
+ display: none;
+}
+
+.ui-widget-overlay {
+ background-color: black;
+ position: fixed;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ opacity: 0.70;
+ -moz-opacity: 0.70;
+ filter: alpha(opacity=70);
+
+ width: 100%;
+ height: 100%;
+}
+
+.ui-front {
+ z-index: 10000;
+}
+
+.settings_page_menu_editor {
+ .ui-dialog {
+ background: white;
+ border: 1px solid #c0c0c0;
+
+ padding: 0;
+
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+
+ .ui-dialog-content {
+ padding: 8px 8px 8px 8px;
+ font-size: 1.1em;
+ }
+
+ .ame-scrollable-dialog-content {
+ max-height: 500px;
+ overflow-y: auto;
+ padding-top: 0.5em;
+ }
+ }
+
+ .ui-dialog-titlebar {
+ display: block;
+ height: 22px;
+ margin: 0;
+ padding: 4px 4px 4px 8px;
+
+ background-color: #86A7E3;
+ font-size: 1.0em;
+ line-height: 22px;
+
+ -webkit-border-top-left-radius: 4px;
+ -webkit-border-top-right-radius: 4px;
+
+ -moz-border-radius-topleft: 4px;
+ -moz-border-radius-topright: 4px;
+
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+
+ border-bottom: 1px solid #809fd9;
+ }
+
+ .ui-dialog-title {
+ color: white;
+ font-weight: bold;
+ }
+
+ .ui-button.ui-dialog-titlebar-close {
+ background: #86A7E3 url(../images/x.png) no-repeat center;
+ width: 22px;
+ height: 22px;
+ display: block;
+ float: right;
+ color: white;
+
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ }
+
+ .ui-dialog-titlebar-close:hover {
+ /*background-image: url(../images/x-light.png);*/
+ background-color: #a6c2f5;
+ }
+
+ .ui-icon-closethick {
+
+ }
+}
+
+#export_dialog .ws_dialog_panel {
+ height: 50px;
+}
+
+#import_dialog .ws_dialog_panel {
+ height: 64px;
+}
+
+.ws_dialog_panel {
+ .ame-fixed-label-text {
+ display: inline-block;
+ min-width: 6em;
+ }
+
+ .ame-inline-select-with-input {
+ vertical-align: baseline;
+ }
+
+ .ame-box-side-sizes {
+ display: flex;
+ flex-wrap: wrap;
+ max-width: 800px;
+
+ .ame-fixed-label-text {
+ min-width: 4em;
+ }
+
+ label {
+ margin-right: 2.5em;
+ }
+
+ input {
+ margin-bottom: 0.4em;
+ }
+
+ input[type=number] {
+ width: 6em;
+ }
+ }
+}
+
+.ame-flexbox-break {
+ flex-basis: 100%;
+ height: 0;
+}
+
+.ws_dialog_buttons {
+ text-align: right;
+ margin-top: 20px;
+ margin-bottom: 1px;
+ clear: both;
+}
+
+.ws_dialog_buttons .button-primary {
+ display: block;
+ float: left;
+ margin-top: 0;
+}
+
+.ws_dialog_buttons .button {
+ margin-top: 0;
+}
+
+.ws_dialog_buttons.ame-vertical-button-list {
+ text-align: left;
+}
+
+.ws_dialog_buttons.ame-vertical-button-list .button-primary {
+ float: none;
+}
+
+.ws_dialog_buttons.ame-vertical-button-list .button {
+ width: 100%;
+ text-align: left;
+ margin-bottom: 10px;
+}
+
+.ws_dialog_buttons.ame-vertical-button-list .button:last-child {
+ margin-bottom: 0;
+}
+
+#import_file_selector {
+ display: block;
+ width: 286px;
+
+ margin: 6px auto 12px;
+}
+
+#ws_start_import {
+ min-width: 100px;
+}
+
+#import_complete_notice {
+ text-align: center;
+ font-size: large;
+ padding-top: 25px;
+}
+
+#ws_import_error_response {
+ width: 100%;
+}
+
+.ws_dont_show_again {
+ display: inline-block;
+ margin-top: 1em;
+}
+
+/************************************
+ Menu access editor
+*************************************/
+
+/* The launch button */
+$accessLevelInputWidth: 190px;
+$accessLevelRightMargin: 5px;
+#ws_menu_editor .ws_edit_field-access_level input.ws_field_value
+{
+ width: $accessLevelInputWidth;
+ margin-right: $accessLevelRightMargin;
+}
+
+.ws_launch_access_editor {
+ min-width: 40px;
+ width: $basicInputWidth - $accessLevelRightMargin - $accessLevelInputWidth - 1px;
+}
+
+#ws_menu_access_editor {
+ width: 400px;
+ display: none;
+}
+
+.ws_dialog_subpanel {
+ margin-bottom: 1em;
+
+ fieldset p {
+ margin-top: 0;
+ margin-bottom: 4px;
+ }
+}
+
+.ws-ame-dialog-subheading {
+ display: block;
+ font-weight: 600;
+ font-size: 1em;
+ margin: 0 0 0.2em 0;
+}
+
+#ws_menu_access_editor .ws_column_access,
+#ws_menu_access_editor .ws_ext_action_check_column {
+ text-align: center;
+ width: 1em;
+ padding-right: 0;
+}
+
+#ws_menu_access_editor .ws_column_access input,
+#ws_menu_access_editor .ws_ext_action_check_column input {
+ margin-right: 0;
+}
+
+#ws_menu_access_editor .ws_column_role {
+ white-space: nowrap;
+}
+
+#ws_role_table_body_container {
+ /*max-height: 400px;
+ overflow: auto;*/
+ overflow: hidden;
+ margin-right: -1px;
+}
+
+.ws_role_table_body {
+ margin-top: 2px;
+ max-width: 354px;
+}
+
+.ws_has_separate_header .ws_role_table_header {
+ border-bottom: none;
+
+ -moz-border-radius-bottomleft: 0;
+ -moz-border-radius-bottomright: 0;
+ -webkit-border-bottom-left-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.ws_has_separate_header .ws_role_table_body {
+ border-top: none;
+ margin-top: 0;
+
+ -moz-border-radius-topleft: 0;
+ -moz-border-radius-topright: 0;
+ -webkit-border-top-left-radius: 0;
+ -webkit-border-top-right-radius: 0;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.ws_role_id {
+ display: none;
+}
+
+#ws_extra_capability {
+ width: 100%;
+}
+
+#ws_role_access_container {
+ position: relative;
+ max-height: 430px;
+ overflow: auto;
+}
+
+#ws_role_access_overlay {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+
+ line-height: 100%;
+
+ background: white;
+ filter: alpha(opacity=60);
+ opacity: 0.6;
+ -moz-opacity:0.6;
+}
+
+#ws_role_access_overlay_content {
+ position: absolute;
+ width: 50%;
+ left: 22%;
+ top: 30%;
+
+ background: white;
+ padding: 8px;
+
+ border: 2px solid silver;
+ border-radius: 5px;
+ color: #555;
+}
+
+#ws_menu_access_editor div.error {
+ margin-left: 0;
+ margin-right: 0;
+ margin-bottom: 5px;
+}
+
+#ws_hardcoded_role_error {
+ display: none;
+}
+
+/*--------------------------------------------*
+ The CPT/taxonomy permissions panel
+ *--------------------------------------------*/
+
+/*
+ * When there are CPT/taxonomy permissions available, the appearance of the role list changes a bit.
+ */
+.ws_has_extended_permissions {
+ //The role name column also functions as a select button.
+ .ws_role_table_body .ws_column_role {
+ cursor: pointer;
+ }
+
+ .ws_role_table_body .ws_column_selected_role_tip {
+ display: table-cell;
+ }
+
+ .ws_role_table_body tr:hover {
+ background: #EAF2FA;
+ }
+
+ .ws_role_table_body {
+ td {
+ border-top: 1px solid #f1f1f1;
+ }
+ tr:first-child td {
+ border-top-width: 0;
+ }
+ }
+
+ /* The role or actor whose CPT/taxonomy permissions are currently expanded. */
+ .ws_role_table_body tr.ws_cpt_selected_role {
+ background-color: #dddddd;
+
+ .ws_column_role {
+ font-weight: bold;
+ }
+
+ .ws_cpt_selected_role_tip {
+ visibility: visible;
+ }
+
+ td {
+ color: #222;
+ }
+ }
+}
+
+#ws_ext_permissions_container {
+ float: left;
+ width: 352px;
+ padding: 0 9px 0 0;
+}
+
+$extendedPanelLeftPadding: 15px;
+
+#ws_ext_permissions_container_caption {
+ padding-left: $extendedPanelLeftPadding;
+ max-width: 352px;
+ position: relative;
+ white-space: nowrap;
+}
+
+#ws_ext_permissions_container .ws_ext_permissions_table {
+ margin-top: 2px;
+
+ tr td:first-child {
+ padding-left: $extendedPanelLeftPadding;
+ }
+
+ .ws_ext_group_title {
+ padding-bottom: 0;
+ font-weight: bold;
+ }
+
+ .ws_ext_action_check_column,
+ .ws_ext_action_name_column {
+ padding-top: 3px;
+ padding-bottom: 3px;
+ }
+
+ tr.ws_ext_padding_row td {
+ padding: 0 0 0 0;
+ height: 1px;
+ }
+
+ .ws_same_as_required_cap {
+ text-decoration: underline;
+ }
+
+ .ws_ext_has_custom_setting {
+ label.ws_ext_action_name::after {
+ content: " *";
+ }
+ }
+}
+
+#ws_ext_permissions_container {
+ //Toggle between readable names and capabilities.
+ //The default is to show readable names (toggle = off), and the alternative is to show capabilities (toggle = on).
+ #ws_ext_toggle_capability_names {
+ cursor: pointer;
+ position: absolute;
+ right: 0;
+ color: #0073aa;
+ }
+
+ &.ws_ext_readable_names_enabled #ws_ext_toggle_capability_names {
+ color: #b4b9be;
+ }
+
+ //State: Show capabilities.
+ .ws_ext_readable_name {
+ display: none;
+ }
+ .ws_ext_capability {
+ display: inline;
+ }
+
+ //State: Show readable names. This is the plugin default.
+ &.ws_ext_readable_names_enabled {
+ .ws_ext_readable_name {
+ display: inline;
+ }
+ .ws_ext_capability {
+ display: none;
+ }
+ }
+}
+
+//The taxonomy table doesn't have capability groups (they're not needed - there are only 4 taxonomy permissions),
+//so its first row needs a bit of extra padding to align vertically with the first role.
+#ws_ext_permissions_container #ws_taxonomy_permissions_table {
+ tr:first-child td {
+ padding-top: 8px;
+ }
+}
+
+/* The "selected role" indicator. */
+.ws_cpt_selected_role_tip {
+ display: block;
+ visibility: hidden;
+
+ box-sizing: border-box;
+ width: 26px;
+ height: 26px;
+
+ position: absolute;
+ right: 0;
+
+ //border: 1px solid #E5E5E5;
+ background: white;
+
+ transform: translate(1px, 0) rotate(-45deg);
+ transform-origin: top right;
+}
+
+.ws_role_table_body .ws_column_selected_role_tip {
+ display: none;
+
+ padding: 0;
+ width: 40px;
+ height: 100%;
+ text-align: right;
+ overflow: visible;
+
+ position: relative;
+
+ cursor: pointer;
+}
+
+.ws_ame_breadcrumb_separator {
+ color: #999;
+}
+
+//The "additional permissions available" notification for individual menu items.
+#ws_menu_editor .ws_ext_permissions_indicator {
+ $indicatorSize: 16px;
+
+ font-size: $indicatorSize;
+ height: $indicatorSize;
+ width: $indicatorSize;
+
+ //Only show when an actor is selected.
+ visibility: hidden;
+
+ vertical-align: bottom;
+ cursor: pointer;
+ color: #4aa100; //Derived from the border-color of an .updated notice. Lab color space, reduced lightness.
+}
+
+#ws_menu_editor.ws_is_actor_view .ws_ext_permissions_indicator {
+ visibility: visible;
+}
+
+/************************************
+ Visible users dialog
+*************************************/
+
+$userSelectionPanelWidth: 350px;
+$userSelectionPanelHeight: 400px;
+$userSelectionPanelPadding: 10px;
+
+#ws_visible_users_dialog {
+ $userDialogPadding: 8px;
+ background: white;
+ padding: $userDialogPadding;
+}
+
+#ws_user_selection_panels {
+ min-width: $userSelectionPanelWidth * 2 + $userSelectionPanelPadding;
+
+ .ws_user_selection_panel {
+ display: block;
+ float: left;
+ position: relative;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ width: $userSelectionPanelWidth;
+ height: $userSelectionPanelHeight;
+
+ border: 1px solid #e5e5e5;
+ margin-right: 10px;
+ padding: 10px;
+ }
+
+ #ws_user_selection_target_panel {
+ margin-right: 0;
+ }
+
+ #ws_available_user_query {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 100%;
+ max-height: 28px;
+ }
+
+ .ws_user_list_wrapper {
+ position: absolute;
+ $userListTop: 50px;
+
+ top: $userListTop;
+ left: $userSelectionPanelPadding;
+ right: $userSelectionPanelPadding;
+
+ //width: $userSelectionPanelWidth - 2 * $userSelectionPanelPadding - 2px;
+ height: $userSelectionPanelHeight - $userListTop - $userSelectionPanelPadding - 2px;
+
+ overflow-x: auto;
+ overflow-y: auto; //Allow scrolling.
+ }
+
+ .ws_user_selection_list {
+ min-height: 20px;
+
+ //No borders. The panels themselves already have borders.
+ border-width: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+
+ .ws_user_action_column {
+ width: 20px;
+ text-align: center;
+
+ padding-top: 9px;
+ padding-bottom: 0;
+ }
+
+ .ws_user_action_button {
+ cursor: pointer;
+ color: #b4b9be;
+ }
+
+ .ws_user_username_column {
+ padding-left: 0;
+ }
+
+ .ws_user_display_name_column {
+ white-space: nowrap;
+ }
+ }
+
+ #ws_available_users {
+ tr {
+ cursor: pointer;
+ }
+
+ tr:hover, tr.ws_user_best_match {
+ background-color: #eaf2fa;
+ }
+
+ //The "add user" button.
+ tr:hover .ws_user_action_button {
+ color: #7ad03a;
+ }
+ }
+
+ #ws_selected_users {
+ //The "remove from list" button.
+ .ws_user_action_button::before {
+ content: "\f158";
+ }
+ .ws_user_action_button:hover {
+ color: #dd3d36;
+ }
+ .ws_user_action_column {
+ padding-left: 6px;
+ }
+
+ .ws_user_display_name_column {
+ display: none;
+ }
+
+ //You can't deselect the current user. It always stays in the visible actor list.
+ tr.ws_user_must_be_selected {
+ .ws_user_action_button {
+ display: none;
+ }
+ }
+
+ td {
+ //padding-bottom: 8px;
+ }
+ tr:not(:first-child) td {
+ //padding-top: 0;
+ }
+
+ }
+
+ #ws_selected_users_caption {
+ font-size: 14px;
+ line-height: 1.4em;
+ padding: 7px 10px;
+
+ color: #555;
+ font-weight: 600;
+ }
+
+ &::after {
+ display: block;
+ height: 1px;
+ visibility: hidden;
+ content: ' ';
+ clear: both;;
+ }
+}
+
+#ws_loading_users_indicator {
+ position: absolute;
+ right: $userSelectionPanelPadding;
+ bottom: $userSelectionPanelPadding;
+
+ margin-right: 0;
+ margin-bottom: 0;
+}
+
+
+
+/************************************
+ Menu deletion error
+*************************************/
+
+#ws-ame-menu-deletion-error {
+ max-width: 400px;
+}
+
+
+
+
+/************************************
+ Tooltips and hints
+*************************************/
+
+.ws_tooltip_trigger, .ws_field_tooltip_trigger {
+ cursor: pointer;
+}
+
+.ws_tooltip_content_list {
+ list-style: disc;
+ margin-left: 1em;
+ margin-bottom: 0;
+}
+
+.ws_tooltip_node {
+ font-size: 13px;
+ line-height: 1.3;
+ border-radius: 3px;
+ max-width: 300px;
+}
+
+.ws_field_tooltip_trigger .dashicons {
+ font-size: 16px;
+ height: 16px;
+ vertical-align: bottom;
+}
+
+.ws_field_tooltip_trigger {
+ color: #a1a1a1;
+}
+
+//Tooltips on the settings page.
+#ws_plugin_settings_form .ws_tooltip_trigger .dashicons {
+ font-size: 18px;
+}
+
+//And in other boxes.
+.ws_ame_custom_postbox .ws_tooltip_trigger .dashicons {
+ font-size: 18px;
+ height: 18px;
+ vertical-align: bottom;
+}
+
+.ws_tooltip_trigger.ame-warning-tooltip {
+ color: orange;
+}
+
+.ws_wide_tooltip {
+ max-width: 450px;
+}
+
+.ws_hint {
+ background: #FFFFE0;
+ border: 1px solid #E6DB55;
+
+ margin-bottom: 0.5em;
+ border-radius: 3px;
+ position: relative;
+ padding-right: 20px;
+}
+
+.ws_hint_close {
+ border: 1px solid #E6DB55;
+ border-right: none;
+ border-top: none;
+ color: #dcc500;
+ font-weight: bold;
+ cursor: pointer;
+
+ width: 18px;
+ text-align: center;
+ border-radius: 3px;
+
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.ws_hint_close:hover {
+ background-color: #ffef4c;
+ border-color: #e0b900;
+ color: black;
+}
+
+.ws_hint_content {
+ padding: 0.4em 0 0.4em 0.4em;
+}
+
+.ws_hint_content ul {
+ list-style: disc;
+ list-style-position: inside;
+ margin-left: 0.5em;
+}
+
+.ws_ame_doc_box, .ws_ame_custom_postbox {
+ .hndle {
+ cursor: default !important;
+ border-bottom: 1px solid $amePostboxBorderColor;
+ }
+
+ .handlediv {
+ display: block;
+ float: right;
+ }
+
+ .inside {
+ margin-bottom: 0;
+ }
+
+ ul {
+ list-style: disc outside;
+ margin-left: 1em;
+ }
+
+ li > ul {
+ margin-top: 6px;
+ }
+
+ .button-link .toggle-indicator::before {
+ margin-top: 4px;
+ width: 20px;
+ -webkit-border-radius: 50%;
+ border-radius: 50%;
+ text-indent: -1px;
+
+ content: "\f142";
+ display: inline-block;
+ font: normal 20px/1 dashicons;
+
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ text-decoration: none !important;
+ }
+
+ &.closed .button-link .toggle-indicator::before {
+ content: "\f140";
+ }
+}
+
+.ws_basic_container .ws_ame_custom_postbox {
+ //Match .ws_main_container's horizontal margins for proper alignment.
+ margin-left: 2px;
+ margin-right: 2px;
+}
+
+.ws_ame_custom_postbox .ame-tutorial-list {
+ margin: 0;
+
+ a {
+ text-decoration: none;
+ display: block;
+ padding: 4px;
+ }
+
+ ul {
+ margin-left: 1em;
+ }
+
+ li {
+ display: block;
+ margin: 0;
+ list-style: none;
+ }
+}
+
+/************************************
+ Copy Permissions dialog
+*************************************/
+#ws-ame-copy-permissions-dialog select {
+ min-width: 280px;
+}
+
+/*********************************************
+ Capability suggestions and preview
+**********************************************/
+
+#ws_capability_suggestions {
+ padding: 4px;
+ width: 350px;
+
+ border: 1px solid #cdd5d5;
+ box-shadow: 0 1px 1px rgba(0,0,0,0.04);
+ background: #fff;
+
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+
+ #ws_previewed_caps {
+ margin-top: 0;
+ margin-bottom: 6px;
+ }
+
+ td, th {
+ //For consistency, padding should match the capability dropdown.
+ padding-top: $dropdownOptionPaddingTop;
+ padding-bottom: $dropdownOptionPaddingTop;
+ }
+
+ tr.ws_preview_has_access .ws_ame_role_name{
+ background-color: lightgreen;
+ }
+
+ .ws_ame_suggested_capability {
+ cursor: pointer;
+
+ &:hover {
+ background-color: #d0f2d0;
+ }
+ }
+}
+
+/*********************************************
+ Settings page stuff
+**********************************************/
+
+#ws_plugin_settings_form figure {
+ margin-left: 0;
+ margin-top: 0;
+ margin-bottom: 1em;
+}
+
+.ame-available-add-ons {
+ tr:first-of-type td {
+ margin-top: 0;
+ padding-top: 0;
+ }
+
+ td {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ }
+
+ .ame-add-on-heading {
+ padding-left: 0;
+ }
+}
+
+.ame-add-on-name {
+ font-weight: 600;
+}
+
+.ame-add-on-details-link::after {
+ /*content: " \f504";
+ font-family: dashicons, sans-serif;*/
+}
+
+/*********************************************
+ WordPress 5.3+ consistent styles
+**********************************************/
+
+.ame-is-wp53-plus .ws_edit_field input[type="button"] {
+ margin-top: 1px;
+}
+
+/*********************************************
+ CSS border style selector
+**********************************************/
+
+.ame-css-border-styles {
+ .ame-fixed-label-text {
+ min-width: 5em;
+ }
+
+ .ame-border-sample-container {
+ display: inline-block;
+ vertical-align: top;
+ min-height: 28px;
+ }
+
+ .ame-border-sample {
+ display: inline-block;
+ width: 14em;
+ border-top: 0.3em solid #444;
+ }
+}
+
+/*********************************************
+ Miscellaneous
+**********************************************/
+
+#ws_sidebar_pro_ad {
+ min-width: 225px;
+
+ margin-top: 5px;
+ margin-left: 3px;
+
+ position: fixed;
+ right: 20px;
+ bottom: 40px;
+ z-index: 100;
+}
+
+.ws-ame-icon-radio-button-group > label {
+ display: inline-block;
+ padding: 8px;
+ border: 1px solid #ccd0d4;
+ border-radius: 2px;
+
+ margin-right: 0.5em;
+}
+
+span.description {
+ color: #666;
+ //Before WP 5.5 description text was displayed in italics. We'll preserve that style for now.
+ font-style: italic;
+}
+
+.wrap :target {
+ background-color: rgba(255, 230, 81, 0.7);
+}
+
+.test-wrap {
+ background-color: #444444;
+ padding: 30px;
+}
+
+.test-container {
+ width: 400px;
+ height: 200px;
+ background-color: white;
+
+ border: 1px solid black;
+ border-radius: 10px;
+
+ overflow: hidden;
+}
+
+.test-header {
+ background-color: #67d6ff;
+ padding: 6px;
+
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+}
+
+.test-content {
+ padding: 8px;
+}
+
+@import "test-access-screen";
+
+@import "main-tabs";
\ No newline at end of file
diff --git a/css/screen-meta-old-wp.css b/css/screen-meta-old-wp.css
new file mode 100644
index 0000000..819c7d1
--- /dev/null
+++ b/css/screen-meta-old-wp.css
@@ -0,0 +1,57 @@
+/************************************
+ Screen meta buttons
+ for WP 3.7 and below
+*************************************/
+
+/* All buttons */
+.custom-screen-meta-link-wrap {
+ float: right;
+ height: 22px;
+ padding: 0;
+ margin: 0 0 0 6px;
+ font-family: sans-serif;
+ -moz-border-radius-bottomleft: 3px;
+ -moz-border-radius-bottomright: 3px;
+ -webkit-border-bottom-left-radius: 3px;
+ -webkit-border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+
+ background: #e3e3e3;
+
+ border-right: 1px solid transparent;
+ border-left: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ background-image: -ms-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* IE10 */
+ background-image: -moz-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Firefox */
+ background-image: -o-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Opera */
+ background-image: -webkit-gradient(linear, left bottom, left top, from(#dfdfdf), to(#f1f1f1)); /* old Webkit */
+ background-image: -webkit-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* new Webkit */
+ background-image: linear-gradient(bottom, #dfdfdf, #f1f1f1); /* proposed W3C Markup */
+}
+
+#screen-meta .custom-screen-meta-link-wrap a.custom-screen-meta-link,
+#screen-meta-links .custom-screen-meta-link-wrap a.custom-screen-meta-link
+{
+ background-image: none;
+ padding: 0 6px 0 6px;
+}
+
+#screen-meta-links a.custom-screen-meta-link::after {
+ display: none;
+}
+
+/* "Upgrade to Pro" */
+#ws-pro-version-notice {
+ background: #00C31F none;
+}
+
+#ws-pro-version-notice a.show-settings {
+ font-weight: bold;
+ color: #DEFFD8;
+ text-shadow: none;
+}
+
+#ws-pro-version-notice a.show-settings:hover {
+ color: white;
+}
\ No newline at end of file
diff --git a/css/screen-meta.css b/css/screen-meta.css
new file mode 100644
index 0000000..286147e
--- /dev/null
+++ b/css/screen-meta.css
@@ -0,0 +1,51 @@
+/************************************
+ Screen meta buttons
+ for WP 3.8+
+*************************************/
+
+/* All buttons */
+.custom-screen-meta-link-wrap {
+ float: right;
+ height: 28px;
+ margin: 0 0 0 6px;
+
+ border: 1px solid #ccd0d4;
+ border-top: none;
+ border-radius: 0 0 4px 4px;
+
+ background: #fff;
+ box-shadow: none;
+}
+
+#screen-meta .custom-screen-meta-link-wrap a.custom-screen-meta-link,
+#screen-meta-links .custom-screen-meta-link-wrap a.custom-screen-meta-link
+{
+ padding: 3px 16px 3px 16px;
+ text-decoration: none;
+ display: block;
+ min-height: 22px;
+ box-shadow: none;
+}
+
+#screen-meta-links a.custom-screen-meta-link::after {
+ display: none;
+}
+
+/* "Upgrade to Pro" */
+#ws-pro-version-notice {
+ background-color: #00C31F;
+ border-color: #0a0;
+}
+
+#screen-meta-links #ws-pro-version-notice a.show-settings {
+ font-weight: bold;
+ color: #DEFFD8;
+ text-shadow: none;
+ box-shadow: none;
+ border: none;
+ background-color: #00C31F;
+}
+
+#screen-meta-links #ws-pro-version-notice a.show-settings:hover {
+ color: white;
+}
\ No newline at end of file
diff --git a/css/style-classic.css b/css/style-classic.css
new file mode 100644
index 0000000..70f5bea
--- /dev/null
+++ b/css/style-classic.css
@@ -0,0 +1,142 @@
+.ws_main_container {
+ padding-bottom: 4px;
+}
+
+.ws_container {
+ border: 1px solid #a9badb;
+ background-color: #bdd3ff;
+}
+
+#ws_menu_editor .ws_active {
+ background-color : #8eb0f1; /* make sure this overrides ws_menu_separator */
+}
+
+#ws_menu_editor.ws_is_actor_view .ws_is_hidden_for_actor.ws_active {
+ background-color : #dadee6;
+}
+
+.ws_menu_separator {
+ background: #F9F9F9 url("../images/menu-arrows.png") no-repeat 4px 8px;
+ border-color: #d9d9d9;
+}
+
+.ws_edit_link {
+ background-image: url('../images/bullet_arrow_down2.png');
+ background-repeat: no-repeat;
+ background-position: center 3px;
+}
+
+a.ws_edit_link:hover {
+ background-color: #ffffd0;
+ background-image: url('../images/bullet_arrow_down2.png');
+}
+
+.ws_edit_link:active {
+ background-repeat: no-repeat;
+ background-position: center 3px;
+}
+
+.ws_edit_link_expanded {
+ background-color: #ffffd0;
+ border-bottom: none;
+ border-color: #ffffd0;
+
+ padding-bottom: 1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+
+ -moz-border-radius-bottomright: 0;
+ -moz-border-radius-bottomleft: 0;
+
+ -webkit-border-bottom-right-radius: 0;
+ -webkit-border-bottom-left-radius: 0;
+}
+
+
+.ws_editbox {
+ background-color: #ffffd0;
+}
+
+.ws_input_default input, .ws_input_default select {
+ color: gray;
+}
+
+/*
+ * Show/Hide advanced fields
+ */
+
+.ws_toggle_advanced_fields {
+ color: #6087CB;
+ font-size: 0.85em;
+}
+
+.ws_toggle_advanced_fields:visited, .ws_toggle_advanced_fields:active {
+ color: #6087CB;
+}
+
+.ws_toggle_advanced_fields:hover {
+ color: #d54e21;
+ text-decoration: underline;
+}
+
+
+/*
+ * Toolbars
+ */
+
+.ws_button {
+ border: 1px solid #c0c0e0;
+}
+
+a.ws_button:hover {
+ background-color: #d0e0ff;
+ border-color: #9090c0;
+}
+
+/************************************
+ Export and import
+*************************************/
+
+.settings_page_menu_editor .ui-dialog {
+ background: white;
+ border: 1px solid #c0c0c0;
+}
+
+.settings_page_menu_editor .ui-dialog-titlebar {
+ background-color: #86A7E3;
+ height: 22px;
+}
+
+.settings_page_menu_editor .ui-dialog-title {
+ color: white;
+}
+
+.settings_page_menu_editor .ui-button.ui-dialog-titlebar-close {
+ background-color: transparent;
+ background-image: none;
+ border-style: none;
+
+ color: white; /* WP default: #666; */
+ cursor: pointer;
+ padding: 0;
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 36px;
+ height: 22px;
+ text-align: center;
+}
+
+.settings_page_menu_editor .ui-dialog-titlebar-close::before {
+ font: normal 20px/30px 'dashicons';
+ content: '\f158';
+
+ vertical-align: top;
+ width: 36px;
+ height: 22px;
+}
+
+.settings_page_menu_editor .ui-dialog-titlebar-close:hover {
+ background: transparent none;
+ color: #004665;
+}
\ No newline at end of file
diff --git a/css/style-modern-one.css b/css/style-modern-one.css
new file mode 100644
index 0000000..dc230ee
--- /dev/null
+++ b/css/style-modern-one.css
@@ -0,0 +1,224 @@
+/*
+//Alternative: like the "invalid" state in the menu customizer.
+$hiddenItemBackground: #f6c9cc;
+$hiddenItemBorder: #f1acb1;
+//*/
+.ws_container {
+ border: 0 solid transparent;
+ background: #fafafa;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 304px;
+ padding: 0;
+ margin-top: 0;
+ margin-bottom: 9px;
+ margin-left: 10px;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
+}
+.ws_container .ws_item_title {
+ color: #23282D;
+ padding-left: 0;
+ font-weight: 600;
+ font-size: 13px;
+}
+.ws_container .ws_item_head {
+ border: 1px solid #dfdfdf;
+ padding: 7px 0 7px 7px;
+}
+.ws_container .ws_flag_container .ws_custom_actor_permissions_flag,
+.ws_container .ws_flag_container .ws_custom_flag {
+ display: none;
+}
+
+#ws_menu_editor.ws_is_actor_view input[type=checkbox].ws_actor_access_checkbox {
+ margin-right: 5px;
+}
+
+.ws_editbox {
+ background: white;
+ padding: 7px;
+ border: 1px solid #dfdfdf;
+ border-top-width: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+a.ws_edit_link {
+ background: transparent;
+ color: #A0A5AA;
+ text-align: center;
+ border-radius: 0;
+}
+a.ws_edit_link::before {
+ content: "\f140";
+ font: normal 20px/1 dashicons;
+ display: block;
+}
+a.ws_edit_link:hover {
+ color: #777;
+}
+
+.ws_edit_link.ws_edit_link_expanded::before {
+ content: "\f142";
+}
+
+.ws_toolbar .ws_button {
+ border: 1px solid #C0C0C0;
+}
+
+.ws_box {
+ margin-top: 6px;
+}
+
+.ws_menu_separator .ws_item_head::after {
+ content: "";
+ display: inline-block;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ vertical-align: middle;
+ width: 249.28px;
+ height: 0;
+ border: 2px inset rgba(0, 0, 0, 0.2);
+}
+.ws_menu_separator .ws_item_title {
+ width: 0;
+ padding-left: 0;
+ padding-right: 0;
+}
+
+.ws_menu.ws_active::after {
+ right: -22px;
+}
+
+.ws_container.ws_active, .ws_container.ws_is_hidden_for_actor.ws_active {
+ z-index: 2;
+}
+.ws_container.ws_active .ws_item_head, .ws_container.ws_is_hidden_for_actor.ws_active .ws_item_head {
+ border-color: #999;
+ background-color: #c7c7c7;
+}
+.ws_container.ws_active .ws_item_title, .ws_container.ws_is_hidden_for_actor.ws_active .ws_item_title {
+ color: #23282D;
+}
+.ws_container.ws_active .ws_editbox, .ws_container.ws_is_hidden_for_actor.ws_active .ws_editbox {
+ border-color: #999;
+}
+
+.ws_container.ws_is_hidden_for_actor .ws_item_head {
+ border-color: #dfdfdf;
+ background-color: #e3e3e3;
+}
+.ws_container.ws_is_hidden_for_actor .ws_editbox {
+ border-color: #dfdfdf;
+}
+.ws_container.ws_is_hidden_for_actor .ws_item_title {
+ color: #888;
+}
+
+.ws_compact_layout .ws_container {
+ margin-top: -1px;
+ margin-bottom: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+.ws_compact_layout .ws_container .ws_item_head {
+ padding-top: 4px;
+ padding-bottom: 4px;
+}
+.ws_compact_layout .ws_container.ws_active {
+ z-index: 2;
+}
+.ws_compact_layout .ws_container:last-child {
+ margin-bottom: 9px;
+}
+.ws_compact_layout.ws_is_hidden_for_actor .ws_item_head, .ws_compact_layout.ws_is_hidden_for_actor .ws_editbox {
+ border-color: #dfdfdf;
+}
+.ws_compact_layout.ws_active .ws_item_head, .ws_compact_layout.ws_active .ws_editbox {
+ border-color: #999;
+}
+
+#ws_menu_editor #ws_toggle_editor_layout {
+ display: block;
+}
+
+#ws_icon_selector, #ws_embedded_page_selector {
+ z-index: 3;
+}
+
+.ws_container.ui-sortable-helper {
+ box-shadow: 1px 3px 6px 0 rgba(1, 1, 1, 0.4);
+}
+
+.ws_main_container {
+ width: 324px;
+}
+.ws_main_container .ws_toolbar {
+ padding: 10px 10px 0;
+}
+.ws_main_container .ws_dropzone {
+ margin-left: 10px;
+ margin-right: 10px;
+}
+
+#ws_editor_sidebar {
+ padding: 6px 10px;
+}
+#ws_editor_sidebar .ws_main_button {
+ margin-left: 0;
+ margin-right: 0;
+}
+
+.settings_page_menu_editor .ui-dialog {
+ background: white;
+ border: 1px solid #c0c0c0;
+ border-radius: 0;
+}
+.settings_page_menu_editor .ui-dialog-titlebar {
+ background-color: #fcfcfc;
+ border-bottom: 1px solid #dfdfdf;
+ height: auto;
+ padding: 0;
+}
+.settings_page_menu_editor .ui-dialog-titlebar .ui-button.ui-dialog-titlebar-close {
+ background: none;
+ border-style: none;
+ color: #666;
+ cursor: pointer;
+ padding: 0;
+ margin: 0;
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 36px;
+ height: 36px;
+ text-align: center;
+}
+.settings_page_menu_editor .ui-dialog-titlebar .ui-button.ui-dialog-titlebar-close .ui-icon, .settings_page_menu_editor .ui-dialog-titlebar .ui-button.ui-dialog-titlebar-close .ui-button-text {
+ display: none;
+}
+.settings_page_menu_editor .ui-dialog-titlebar .ui-dialog-titlebar-close::before {
+ font: normal 20px/36px "dashicons";
+ content: "\f158";
+ vertical-align: middle;
+ width: 36px;
+ height: 36px;
+}
+.settings_page_menu_editor .ui-dialog-titlebar .ui-dialog-titlebar-close:hover {
+ background: transparent none;
+ color: #2ea2cc;
+}
+.settings_page_menu_editor .ui-dialog-title {
+ color: #444444;
+ font-size: 18px;
+ font-weight: 600;
+ line-height: 36px;
+ padding: 0 36px 0 8px;
+ display: block;
+}
+
+/*# sourceMappingURL=style-modern-one.css.map */
diff --git a/css/style-modern-one.css.map b/css/style-modern-one.css.map
new file mode 100644
index 0000000..b79d916
--- /dev/null
+++ b/css/style-modern-one.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["style-modern-one.scss"],"names":[],"mappings":"AAmBA;AAAA;AAAA;AAAA;AAAA;AAaA;EAGC;EACA,YApCgB;EAsChB;EACA;EACA;EAEA,OA9BW;EA+BX;EAEA;EACA,eAjCkB;EAkClB,aAduB;EAgBvB;;AAEA;EACC,OA9CS;EA+CT;EACA;EACA;;AAGD;EACC;EACA;;AAMA;AAAA;EAEC;;;AAKH;EACC;;;AAGD;EACC;EACA,SArEmB;EAuEnB;EACA;EAEA;EACA;EACA;;;AAGD;EACC;EACA;EAEA;EACA;;AAEA;EACC;EACA;EACA;;AAGD;EACC;;;AAIF;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAQA;EACC;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAGA;;AAGD;EACC;EACA;EACA;;;AAKF;EACC;;;AAMD;EACC;;AAEA;EACC,cA9ImB;EA+InB,kBAhJuB;;AAmJxB;EACC,OAlJiB;;AAqJlB;EACC,cAvJmB;;;AA+JpB;EACC,cA/KW;EAgLX,kBAxJqB;;AA2JtB;EACC,cApLW;;AAuLZ;EACC,OA9Je;;;AA0KhB;EACC;EACA;EAEA;EACA;EACA;;AAEA;EACC,aAXuB;EAYvB,gBAZuB;;AAqBxB;EACC;;AAIF;EACC,eAjNiB;;AAqNjB;EACC,cAlOU;;AAsOX;EACC,cAxNkB;;;AA6NrB;EACC;;;AAOD;EACC;;;AAOD;EACC;;;AAOD;EACC,OAxOoB;;AA0OpB;EACC;;AAGD;EACC,aAhPc;EAiPd,cAjPc;;;AAqPhB;EACC;;AAEA;EACC;EACA;;;AAUD;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;;AAEA;EACC;;AAIF;EACC;EACA;EAEA;EACA;EACA;;AAGD;EACC;EACA;;AAIF;EACC;EACA;EACA;EACA;EAEA;EACA","file":"style-modern-one.css"}
\ No newline at end of file
diff --git a/css/style-modern-one.scss b/css/style-modern-one.scss
new file mode 100644
index 0000000..08c8fd2
--- /dev/null
+++ b/css/style-modern-one.scss
@@ -0,0 +1,348 @@
+$itemBackground: #fafafa;
+$itemBorder: #dfdfdf;
+
+//$itemBackground: #f7f7f7;
+//$itemBorder: #cacaca;
+
+$itemText: #23282D; //WordPress default.
+//$itemText: #5a5a5a; //CodePress default.
+//$itemText: #555; //Theme customizer widget headings.
+
+$horizontalPadding: 7px;
+$headVerticalPadding: 7px;
+$itemWidth: 304px;
+$itemMarginBottom: 9px;
+
+$selectedItemBackground: #c7c7c7;
+$selectedItemBorder: #999;
+$selectedItemText: #23282D;
+
+/*
+//Alternative: like the "invalid" state in the menu customizer.
+$hiddenItemBackground: #f6c9cc;
+$hiddenItemBorder: #f1acb1;
+//*/
+
+$hiddenItemBackground: darken($itemBackground, 9);
+$hiddenItemBorder: $itemBorder;
+$hiddenItemText: #888; //#9a9ea5; //#82878C; //#999
+
+$columnPadding: 10px;
+$mainContainerWidth: $itemWidth + $columnPadding * 2;
+
+.ws_container {
+ $itemHorizontalMargin: ($mainContainerWidth - $itemWidth) / 2;
+
+ border: 0 solid transparent;
+ background: $itemBackground;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ width: $itemWidth;
+ padding: 0;
+
+ margin-top: 0;
+ margin-bottom: $itemMarginBottom;
+ margin-left: $itemHorizontalMargin;
+
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
+
+ .ws_item_title {
+ color: $itemText;
+ padding-left: 0;
+ font-weight: 600;
+ font-size: 13px;
+ }
+
+ .ws_item_head {
+ border: 1px solid $itemBorder;
+ padding: $headVerticalPadding 0 $headVerticalPadding $horizontalPadding;
+ }
+
+ .ws_flag_container {
+ //Hide low-importance flags. It's debatable (knowing which roles have custom permissions is useful
+ //when debugging configuration issues), but lets leave them hidden for now.
+ .ws_custom_actor_permissions_flag,
+ .ws_custom_flag {
+ display: none;
+ }
+ }
+}
+
+#ws_menu_editor.ws_is_actor_view input[type="checkbox"].ws_actor_access_checkbox {
+ margin-right: 5px;
+}
+
+.ws_editbox {
+ background: white;
+ padding: $horizontalPadding;
+
+ border: 1px solid $itemBorder;
+ border-top-width: 0;
+
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+a.ws_edit_link {
+ background: transparent;
+ color: #A0A5AA;
+
+ text-align: center;
+ border-radius: 0;
+
+ &::before {
+ content: "\f140";
+ font: normal 20px/1 dashicons;
+ display: block;
+ }
+
+ &:hover {
+ color: #777;
+ }
+}
+
+.ws_edit_link.ws_edit_link_expanded::before {
+ content: "\f142";
+}
+
+.ws_toolbar .ws_button {
+ border: 1px solid #C0C0C0;
+}
+
+.ws_box {
+ margin-top: 6px;
+}
+
+.ws_menu_separator {
+ .ws_item_head {
+ //background: url("../images/menu-arrows.png") no-repeat ($horizontalPadding + 22px) center;
+ }
+
+ .ws_item_head::after {
+ content: '';
+ display: inline-block;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ vertical-align: middle;
+ width: $itemWidth * 0.82;
+ height: 0;
+ $separatorColor: rgba(0, 0, 0, 0.2);
+
+ border: 2px inset $separatorColor;
+ }
+
+ .ws_item_title {
+ width: 0;
+ padding-left: 0;
+ padding-right: 0;
+ }
+}
+
+
+.ws_menu.ws_active::after {
+ right: -22px;
+}
+
+//==============================================
+// Selected state
+//==============================================
+.ws_container.ws_active, .ws_container.ws_is_hidden_for_actor.ws_active {
+ z-index: 2;
+
+ .ws_item_head {
+ border-color: $selectedItemBorder;
+ background-color: $selectedItemBackground;
+ }
+
+ .ws_item_title {
+ color: $selectedItemText;
+ }
+
+ .ws_editbox {
+ border-color: $selectedItemBorder;
+ }
+}
+
+//==============================================
+// Hidden state
+//==============================================
+.ws_container.ws_is_hidden_for_actor {
+ .ws_item_head {
+ border-color: $hiddenItemBorder;
+ background-color: $hiddenItemBackground;
+ }
+
+ .ws_editbox {
+ border-color: $hiddenItemBorder;
+ }
+
+ .ws_item_title {
+ color: $hiddenItemText;
+ }
+}
+
+//==============================================
+// Compact layout option
+//==============================================
+
+.ws_compact_layout {
+ $compactBorderColor: $itemBorder; //Alternative: #cacaca
+ $compactVerticalPadding: 4px;
+
+ .ws_container {
+ margin-top: -1px;
+ margin-bottom: 0;
+
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+
+ .ws_item_head {
+ padding-top: $compactVerticalPadding;
+ padding-bottom: $compactVerticalPadding;
+ }
+
+ .ws_item_title {
+ //Alternative: thinner, less in-your-face title text.
+ //font-weight: normal;
+ //font-size: 14px;
+ }
+
+ &.ws_active {
+ z-index: 2;
+ }
+ }
+
+ .ws_container:last-child {
+ margin-bottom: $itemMarginBottom;
+ }
+
+ &.ws_is_hidden_for_actor {
+ .ws_item_head, .ws_editbox {
+ border-color: $hiddenItemBorder;
+ }
+ }
+ &.ws_active {
+ .ws_item_head, .ws_editbox {
+ border-color: $selectedItemBorder;
+ }
+ }
+}
+
+#ws_menu_editor #ws_toggle_editor_layout {
+ display: block;
+}
+
+//====================================================
+// Dropdowns should appear above the selected item.
+//====================================================
+
+#ws_icon_selector, #ws_embedded_page_selector {
+ z-index: 3;
+}
+
+//==============================================
+// Item dragging
+//==============================================
+
+.ws_container.ui-sortable-helper {
+ box-shadow: 1px 3px 6px 0 rgba(1, 1, 1, 0.4);
+}
+
+//==============================================
+// Columns / containers
+//==============================================
+
+.ws_main_container {
+ width: $mainContainerWidth;
+
+ .ws_toolbar {
+ padding: $columnPadding $columnPadding 0;
+ }
+
+ .ws_dropzone {
+ margin-left: $columnPadding;
+ margin-right: $columnPadding;
+ }
+}
+
+#ws_editor_sidebar {
+ padding: ($columnPadding - 4px) $columnPadding;
+
+ .ws_main_button {
+ margin-left: 0;
+ margin-right: 0;
+ }
+}
+
+
+//==============================================
+// Dialogs
+//==============================================
+
+.settings_page_menu_editor {
+ .ui-dialog {
+ background: white;
+ border: 1px solid #c0c0c0;
+ border-radius: 0;
+ }
+
+ .ui-dialog-titlebar {
+ background-color: #fcfcfc;
+ border-bottom: 1px solid #dfdfdf;
+ height: auto;
+ padding: 0;
+
+ .ui-button.ui-dialog-titlebar-close {
+ background: none;
+ border-style: none;
+
+ color: #666;
+ cursor: pointer;
+ padding: 0;
+ margin: 0;
+ position: absolute;
+ top: 0;
+ right: 0;
+
+ width: 36px;
+ height: 36px;
+
+ text-align: center;
+
+ .ui-icon, .ui-button-text {
+ display: none;
+ }
+ }
+
+ .ui-dialog-titlebar-close::before {
+ font: normal 20px/36px 'dashicons';
+ content: '\f158';
+
+ vertical-align: middle;
+ width: 36px;
+ height: 36px;
+ }
+
+ .ui-dialog-titlebar-close:hover {
+ background: transparent none;
+ color: #2ea2cc;
+ }
+ }
+
+ .ui-dialog-title {
+ color: #444444;
+ font-size: 18px;
+ font-weight: 600;
+ line-height: 36px;
+
+ padding: 0 36px 0 8px;
+ display: block;
+ }
+}
diff --git a/css/style-wp-grey.css b/css/style-wp-grey.css
new file mode 100644
index 0000000..223d360
--- /dev/null
+++ b/css/style-wp-grey.css
@@ -0,0 +1,245 @@
+.ws_container {
+ padding: 0;
+ width: 296px;
+ margin-bottom: 5px;
+
+ background: white;
+
+ border: 1px solid #aeaeae;
+
+ -webkit-box-shadow: inset 0 1px 0 #fff;
+ box-shadow: inset 0 1px 0 #fff;
+
+ /*-webkit-border-radius: 2px;
+ border-radius: 2px;*/
+}
+
+/**
+ * Item head elements
+ */
+
+.ws_item_head {
+ padding: 3px;
+
+ background-color: #d9d9d9;
+ background-image: -ms-linear-gradient(top, #e9e9e9, #d9d9d9);
+ background-image: -moz-linear-gradient(top, #e9e9e9, #d9d9d9);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#e9e9e9), to(#d9d9d9));
+ background-image: -webkit-linear-gradient(top, #e9e9e9, #d9d9d9);
+ background-image: linear-gradient(to bottom, #e9e9e9, #d9d9d9);
+}
+
+.ws_item_title {
+ color: #222;
+ text-shadow: #FFFFFF 0 1px 0;
+}
+
+/**
+ * The down-arrow that expands menu settings
+ */
+
+.ws_edit_link {
+ background: transparent url(../images/arrows.png) no-repeat center 3px;
+ overflow: hidden;
+ text-indent:-999em;
+}
+
+a.ws_edit_link:hover {
+ background-image: url(../images/arrows-dark.png);
+}
+
+.ws_edit_link:active {
+ background-image: url(../images/arrows-dark.png);
+}
+
+.ws_edit_link_expanded {
+ border-bottom: none;
+ border-color: #ffffd0;
+
+ padding-bottom: 1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+
+ -moz-border-radius-bottomright: 0;
+ -moz-border-radius-bottomleft: 0;
+
+ -webkit-border-bottom-right-radius: 0;
+ -webkit-border-bottom-left-radius: 0;
+}
+
+
+/**
+ * Separators
+ */
+
+.ws_menu_separator {
+ border-color: #d9d9d9;
+}
+
+.ws_menu_separator .ws_item_head {
+ min-height: 22px;
+ background: #F9F9F9 url("../images/menu-arrows.png") no-repeat 4px 8px;
+}
+
+.ws_menu_separator.ws_active .ws_item_head {
+ background: #999 url("../images/menu-arrows.png") no-repeat 4px 8px;
+}
+
+/* Offset the separator image in actor view to prevent it from overlapping the checkbox. */
+.ws_is_actor_view .ws_menu_separator .ws_item_head {
+ background-position: 25px 8px;
+}
+
+/**
+ * Active item
+ */
+
+.ws_active .ws_item_head {
+ background: #777;
+ background-image: -webkit-gradient(linear, left bottom, left top, from(#6d6d6d), to(#808080));
+ background-image: -webkit-linear-gradient(bottom, #6d6d6d, #808080);
+ background-image: -moz-linear-gradient(bottom, #6d6d6d, #808080);
+ background-image: -o-linear-gradient(bottom, #6d6d6d, #808080);
+ background-image: linear-gradient(to top, #6d6d6d, #808080);
+}
+
+.ws_active .ws_item_title {
+ text-shadow: 0 -1px 0 #333;
+ color: #fff;
+ border-top-color: #808080;
+ border-bottom-color: #6d6d6d;
+}
+
+/**
+ * Hidden items.
+ */
+.ws_is_actor_view .ws_container.ws_is_hidden_for_actor .ws_item_head {
+ background: #F9F9F9 linear-gradient(to top, #F3F3F3, #FFFFFF);
+}
+
+/* selected hidden items */
+.ws_is_actor_view .ws_is_hidden_for_actor.ws_active .ws_item_head {
+ background: #dedede linear-gradient(to top, #8f8f8f, #a2a2a2);
+}
+.ws_is_actor_view .ws_is_hidden_for_actor.ws_active .ws_item_title {
+ color: #fff;
+ text-shadow: none;
+}
+
+/* hidden separators */
+.ws_is_actor_view .ws_menu_separator.ws_is_hidden_for_actor .ws_item_head {
+ /* Override gradient with the separator image. */
+ background: url("../images/menu-arrows.png") no-repeat 25px 8px;
+}
+/* selected hidden separators */
+.ws_menu_separator.ws_is_hidden_for_actor.ws_active .ws_item_head {
+ background: #aaa url("../images/menu-arrows.png") no-repeat 25px 8px;
+}
+
+
+/**
+ * Dropping menus on other menus.
+ */
+
+.ws_menu_drop_hover, .ws_menu_drop_hover .ws_item_head {
+ background: #43b529;
+}
+
+.ws_menu_drop_hover .ws_item_title {
+ text-shadow: none;
+}
+
+/**
+ * Misc
+ */
+
+.ws_editbox {
+ /*background-color: #ffffd0;*/
+ background-color: #FBFBFB;
+ padding: 4px 6px;
+}
+
+.ws_input_default input, .ws_input_default select {
+ color: gray;
+}
+
+/*
+ * Show/Hide advanced fields
+ */
+
+.ws_toggle_advanced_fields {
+ color: #6087CB;
+ font-size: 0.85em;
+}
+
+.ws_toggle_advanced_fields:visited, .ws_toggle_advanced_fields:active {
+ color: #6087CB;
+}
+
+.ws_toggle_advanced_fields:hover {
+ color: #d54e21;
+ text-decoration: underline;
+}
+
+
+/*
+ * Toolbars
+ */
+
+.ws_button {
+ border: 1px solid #c0c0e0;
+}
+
+a.ws_button:hover {
+ background-color: #d0e0ff;
+ border-color: #9090c0;
+}
+
+/************************************
+ Export and import
+*************************************/
+
+.ui-dialog {
+ background: white;
+ border: 1px solid #c0c0c0;
+
+ border-radius: 0;
+}
+
+.ui-dialog-titlebar {
+ background-color: #fcfcfc;;
+ border-bottom-color: #dfdfdf;
+}
+
+.ui-dialog-title {
+ color: #444444;
+}
+
+.ui-dialog-titlebar-close {
+ background-color: transparent;
+ border-style: none;
+
+ color: #666;
+ cursor: pointer;
+ padding: 0;
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 36px;
+ height: 30px;
+ text-align: center;
+}
+
+.ui-dialog-titlebar-close::before {
+ font: normal 20px/30px 'dashicons';
+ content: '\f158';
+
+ vertical-align: top;
+ width: 36px;
+ height: 30px;
+}
+
+.ui-dialog-titlebar-close:hover {
+ background: transparent none;
+ color: #2ea2cc;
+}
\ No newline at end of file
diff --git a/extras.php b/extras.php
new file mode 100644
index 0000000..368fb95
--- /dev/null
+++ b/extras.php
@@ -0,0 +1,2595 @@
+wp_menu_editor = $wp_menu_editor;
+
+ add_filter('admin_menu_editor-available_modules', array($this, 'filter_available_modules'), 10, 1);
+
+ //Multisite: Clear caches when switching to another site.
+ add_action('switch_blog', array($this, 'clear_site_specific_caches'), 10, 0);
+
+ //Apply most Pro version menu customizations all in one go. This reduces apply_filters() overhead
+ //and is slightly faster than adding a separate filter for each feature.
+ add_filter('custom_admin_menu', array($this, 'apply_admin_menu_filters'));
+ add_filter('custom_admin_submenu', array($this, 'apply_admin_menu_filters'), 10, 2);
+
+ add_action('admin_menu_editor-menu_merged', array($this, 'on_menu_merged'), 10, 1);
+ add_action('admin_menu_editor-menu_built', array($this, 'on_menu_built'), 10, 2);
+
+ //Add some extra shortcodes of our own
+ $shortcode_callback = array($this, 'handle_shortcode');
+ $info_shortcodes = array(
+ 'wp-wpurl', //WordPress address (URI), as returned by get_bloginfo()
+ 'wp-siteurl', //Blog address (URI)
+ 'wp-admin', //Admin area URL (with a trailing slash)
+ 'wp-name', //Weblog title
+ 'wp-version', //Current WP version
+ 'wp-user-display-name', //Current user's display name,
+ 'wp-user-first-name', // first name,
+ 'wp-user-last-name', // last name,
+ 'wp-user-login', // and username/login.
+ 'wp-logout-url', //A URL that lets the current user log out.
+ );
+ foreach($info_shortcodes as $tag){
+ add_shortcode($tag, $shortcode_callback);
+ }
+ add_shortcode('ame-count-bubble', array($this, 'handle_count_shortcode'));
+
+ //Output the menu-modification JS after the menu has been generated.
+ //'in_admin_header' is, AFAIK, the action that fires the soonest after menu
+ //output has been completed, so we use that.
+ add_action('in_admin_header', array($this, 'fix_flagged_menus'));
+
+ //Import/export settings
+ $this->export_settings = array(
+ 'max_file_size' => 5*1024*1024,
+ 'file_extension' => 'dat',
+ 'old_format_string' => 'wsMenuEditor_ExportFile',
+ );
+
+ //Insert the import and export dialog HTML into the editor's page
+ add_action('admin_menu_editor-footer', array($this, 'menu_editor_footer'));
+ //Handle menu downloads and uploads
+ add_action('admin_menu_editor-header', array($this, 'menu_editor_header'));
+ //Handle export requests
+ add_action( 'wp_ajax_export_custom_menu', array($this,'ajax_export_custom_menu') );
+ //Add the "Import" and "Export" buttons
+ add_action('admin_menu_editor-sidebar', array($this, 'add_extra_buttons'));
+
+ //Initialise the universal import/export handler.
+ wsAmeImportExportFeature::get_instance($this->wp_menu_editor);
+
+ add_filter('admin_menu_editor-self_page_title', array($this, 'pro_page_title'), 10, 0);
+ add_filter('admin_menu_editor-self_menu_title', array($this, 'pro_menu_title'), 10, 0);
+
+ //Let other components know we're Pro.
+ add_filter('admin_menu_editor_is_pro', array($this, 'is_pro_version'), 10, 0);
+
+ //Add menu item drop zones to the top-level and sub-menu containers.
+ add_action('admin_menu_editor-container', array($this, 'output_menu_dropzone'), 10, 1);
+
+ //Add submenu icons.
+ add_filter('admin_menu_editor-submenu_with_icon', array($this, 'add_submenu_icon_html'), 10, 2);
+
+ //Multisite: Let people edit the network admin menu.
+ add_action(
+ 'network_admin_menu',
+ array($this->wp_menu_editor, 'hook_admin_menu'),
+ $this->wp_menu_editor->get_magic_hook_priority()
+ );
+
+ //Add extra scripts to the menu editor.
+ add_action('admin_menu_editor-register_scripts', array($this, 'register_extra_scripts'));
+ add_filter('admin_menu_editor-editor_script_dependencies', array($this, 'add_extra_editor_dependencies'));
+
+ /**
+ * Access management extensions.
+ */
+
+ //Allow usernames to be used in capability checks. Syntax : "user:user_login"
+ add_filter('admin_menu_editor-virtual_caps', array($this, 'add_user_actor_cap'), 10, 2);
+
+ //Enable advanced capability operations (OR, AND, NOT) for internal use.
+ add_filter('admin_menu_editor-current_user_can', array($this, 'grant_computed_caps_to_current_user'), 10, 2);
+
+ //Custom per-role and per-user access settings (distinct from the "extra capability" field.
+ add_filter('custom_admin_menu_capability', array($this, 'apply_custom_access'));
+
+ //Role access: Grant virtual capabilities to roles/users that need them to access certain menus.
+ add_filter('admin_menu_editor-virtual_caps', array($this, 'prepare_virtual_caps_for_user'), 10, 2);
+ add_filter('role_has_cap', array($this, 'grant_virtual_caps_to_role'), 200, 3);
+
+ add_action('load-options.php', array($this, 'disable_virtual_caps_on_all_options'));
+
+ //Make it possible to automatically hide new admin menus.
+ add_filter('admin_menu_editor-new_menu_grant_access', array($this, 'get_new_menu_grant_access'));
+
+ //Remove the plugin from the "Plugins" page for users who're not allowed to see it.
+ if ( $this->wp_menu_editor->get_plugin_option('plugins_page_allowed_user_id') !== null ) {
+ add_filter('all_plugins', array($this, 'filter_plugin_list'));
+ }
+
+ /**
+ * Menu color scheme generation.
+ */
+ add_filter('ame_pre_set_custom_menu', array($this, 'add_menu_color_css'));
+ add_action('wp_ajax_ame_output_menu_color_css', array($this,'ajax_output_menu_color_css') );
+
+ //FontAwesome icons.
+ add_filter('custom_admin_menu', array($this, 'add_menu_fa_icon'), 10, 1);
+ add_filter('admin_menu_editor-icon_selector_tabs', array($this, 'add_fa_selector_tab'), 10, 1);
+ add_action('admin_menu_editor-icon_selector', array($this, 'output_fa_selector_tab'));
+
+ //Menu headings.
+ if ( (is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) && version_compare(PHP_VERSION, '5.6', '>=') ) {
+ require_once AME_ROOT_DIR . '/extras/menu-headings/ameMenuHeadingStyler.php';
+ new ameMenuHeadingStyler($this->wp_menu_editor);
+ }
+
+ //License management
+ add_filter('wslm_license_ui_title-admin-menu-editor-pro', array($this, 'license_ui_title'), 10, 0);
+ add_action('wslm_license_ui_logo-admin-menu-editor-pro', array($this, 'license_ui_logo'));
+ add_action('wslm_license_ui_details-admin-menu-editor-pro', array($this, 'license_ui_upgrade_link'), 10, 3);
+ add_filter('wslm_product_name-admin-menu-editor-pro', array($this, 'license_ui_product_name'), 10, 0);
+
+ /**
+ * Add scripts and styles that apply to all admin pages.
+ */
+ add_action('admin_enqueue_scripts', array($this, 'enqueue_dashboard_deps'));
+
+ /**
+ * Add-on display and installation.
+ */
+ //Disabled for now. This feature should be finished later.
+ /*
+ add_action('admin-menu-editor-display_addons', array($this, 'display_addons'));
+ add_action('admin_menu_editor-enqueue_scripts-settings', array($this, 'enqueue_addon_scripts'));
+
+ add_action('wp_ajax_ws_ame_activate_add_on', array($this, 'ajax_activate_addon'));
+ add_action('wp_ajax_ws_ame_activate_add_on', array($this, 'ajax_install_addon'));
+ //*/
+ }
+
+ /**
+ * Process shortcodes in menu fields
+ *
+ * @param array $item
+ * @return array
+ */
+ function do_shortcodes($item){
+ foreach($this->fields_supporting_shortcodes as $field){
+ if ( isset($item[$field]) ) {
+ $value = $item[$field];
+ if ( strpos($value, '[') !== false ){
+ $this->current_shortcode_item = $item;
+ $item[$field] = do_shortcode($value);
+ $this->current_shortcode_item = null;
+ }
+ }
+ }
+ return $item;
+ }
+
+ /**
+ * Get the value of one of our extra shortcodes
+ *
+ * @param array $atts Shortcode attributes (ignored)
+ * @param string $content Content enclosed by the shortcode (ignored)
+ * @param string $code
+ * @return string Shortcode will be replaced with this value
+ */
+ function handle_shortcode($atts, /** @noinspection PhpUnusedParameterInspection */ $content = null, $code = ''){
+ //The shortcode tag can be either $code or the zeroth member of the $atts array.
+ if ( empty($code) ){
+ $code = isset($atts[0]) ? $atts[0] : '';
+ }
+
+ $info = '['.$code.']'; //Default value
+ switch($code){
+ case 'wp-wpurl':
+ $info = get_bloginfo('wpurl');
+ break;
+
+ case 'wp-siteurl':
+ $info = get_bloginfo('url');
+ break;
+
+ case 'wp-admin':
+ $info = admin_url();
+ break;
+
+ case 'wp-name':
+ $info = get_bloginfo('name');
+ break;
+
+ case 'wp-version':
+ $info = get_bloginfo('version');
+ break;
+
+ case 'wp-user-display-name':
+ $info = $this->get_current_user_property('display_name');
+ break;
+
+ case 'wp-user-first-name':
+ $info = $this->get_current_user_property('first_name');
+ break;
+
+ case 'wp-user-last-name':
+ $info = $this->get_current_user_property('last_name');
+ break;
+
+ case 'wp-user-login':
+ $info = $this->get_current_user_property('user_login');
+ break;
+
+ case 'wp-logout-url':
+ $info = wp_logout_url();
+ break;
+ }
+
+ return $info;
+ }
+
+ private function get_current_user_property($property) {
+ $user = wp_get_current_user();
+ if (is_object($user)) {
+ return strval($user->get($property));
+ }
+ return '';
+ }
+
+ /**
+ * Get the HTML code for the small "(123)" bubble in the title of the current menu item.
+ *
+ * The count bubble shortcode is intended for situations where the user wants to rename
+ * a menu item like "WooCommerce -> Orders" that includes a small bubble showing the number
+ * of pending orders (or plugin updates, comments awaiting moderation, etc). The shortcode
+ * extracts the count from the default menu title and shows it in the custom title.
+ *
+ * @return string
+ */
+ public function handle_count_shortcode() {
+ if (isset(
+ $this->current_shortcode_item,
+ $this->current_shortcode_item['defaults'],
+ $this->current_shortcode_item['defaults']['menu_title']
+ )) {
+ //Oh boy, this is excessive! Tests say it takes < 1 ms per shortcode,
+ //but it still seems wrong to go this far just to extract a tag.
+ $title = $this->current_shortcode_item['defaults']['menu_title'];
+ if ( stripos($title, 'loadHTML($title) ) {
+ /** @noinspection PhpComposerExtensionStubsInspection */
+ $xpath = new DOMXpath($dom);
+ $result = $xpath->query('//span[contains(@class,"update-plugins") or contains(@class,"awaiting-mod")]');
+ if ( $result->length > 0 ) {
+ $span = $result->item(0);
+ return $span->ownerDocument->saveHTML($span);
+ }
+ }
+ }
+ }
+ return '';
+ }
+
+ /**
+ * Flag menus (and menu items) that are set to open in a new window
+ * so that they can be identified later.
+ *
+ * Adds a element to the title
+ * of each detected menu.
+ *
+ * @param array $item
+ * @return array
+ */
+ function flag_new_window_menus($item){
+ $open_in = ameMenuItem::get($item, 'open_in', 'same_window');
+ if ( $open_in == 'new_window' ){
+ $old_title = ameMenuItem::get($item, 'menu_title', '');
+ $item['menu_title'] = $old_title . '';
+
+ //For compatibility with Ozh's Admin Drop Down menu, record the link ID that will be
+ //assigned to this item. This lets us modify it later.
+ if ( function_exists('wp_ozh_adminmenu_sanitize_id') ){
+ $subid = 'oamsub_'.wp_ozh_adminmenu_sanitize_id(
+ ameMenuItem::get($item, 'file', '')
+ );
+ $this->ozhs_new_window_menus[] = '#' . str_replace(
+ array(':', '&'),
+ array('\\\\:', '\\\\&'),
+ $subid
+ );
+ }
+ }
+
+ return $item;
+ }
+
+ /**
+ * Output a piece of JS that will find flagged menu links and make them
+ * open in a new window.
+ *
+ * @return void
+ */
+ function fix_flagged_menus(){
+ ?>
+
+ framed_pages[$slug] = $item; //Used by the callback function
+
+ //Default to using menu title for page title, if no custom title specified
+ if ( empty($item['page_title']) ) {
+ $item['page_title'] = $item['menu_title'];
+ }
+
+ //Add a virtual menu. The menu record created by add_menu_page will be
+ //thrown away; what matters is that this populates other structures
+ //like $_registered_pages.
+ add_menu_page(
+ $item['page_title'],
+ $item['menu_title'],
+ $item['access_level'],
+ $slug,
+ array($this, 'display_framed_page')
+ );
+
+ //Change the slug to our newly created page.
+ $item['file'] = $slug;
+ }
+
+ return $item;
+ }
+
+ /**
+ * Intercept menu items that need to be displayed in an IFrame.
+ *
+ * @see wsMenuEditorExtras::create_framed_menu()
+ *
+ * @param array $item
+ * @param string $parent_file
+ * @return array
+ */
+ function create_framed_item($item, $parent_file = null){
+ if ( ($item['open_in'] == 'iframe') && !empty($parent_file) ){
+
+ $slug = 'framed-menu-item-' . md5($item['file'] . '|' . $parent_file);
+ $this->framed_pages[$slug] = $item;
+
+ if ( empty($item['page_title']) ) {
+ $item['page_title'] = $item['menu_title'];
+ }
+ add_submenu_page(
+ $parent_file,
+ $item['page_title'],
+ $item['menu_title'],
+ $item['access_level'],
+ $slug,
+ array($this, 'display_framed_page')
+ );
+
+ $item['file'] = $slug;
+ }
+
+ return $item;
+ }
+
+ /**
+ * Display a page in an IFrame.
+ * This callback is used by all menu items that are set to open in a frame.
+ *
+ * @return void
+ */
+ function display_framed_page(){
+ global $plugin_page;
+
+ if ( isset($this->framed_pages[$plugin_page]) ){
+ $item = $this->framed_pages[$plugin_page];
+ } else {
+ return;
+ }
+
+ if ( !current_user_can($item['access_level'], -1) ){
+ echo "You do not have sufficient permissions to view this page.";
+ return;
+ }
+
+ $styles = array(
+ 'border' => '0 none',
+ 'width' => '100%',
+ 'min-height' => '300px',
+ );
+
+ //The user can set the frame height manually or let the plugin calculate it automatically (the default).
+ $height = !empty($item['iframe_height']) ? intval($item['iframe_height']) : 0;
+ $height = min(max($height, 0), 10000);
+ if ( !empty($height) ) {
+ $styles['height'] = $height . 'px';
+ unset($styles['min-height']);
+ }
+
+ $is_scrolling_disabled = !empty($item['is_iframe_scroll_disabled']);
+
+ $style_attr = '';
+ foreach($styles as $property => $value) {
+ $style_attr .= $property . ': ' . $value . ';';
+ }
+
+ $heading = !empty($item['page_title'])?$item['page_title']:$item['menu_title'];
+ $heading = sprintf('<%1$s>%2$s%1$s>', WPMenuEditor::$admin_heading_tag, $heading);
+ ?>
+ Error: Invalid page. How did you get here?
';
+ return;
+ }
+
+ $item = $this->embedded_wp_pages[$slug];
+ $page_id = ameMenuItem::get($item, 'embedded_page_id', 0);
+ $page_blog_id = ameMenuItem::get($item, 'embedded_page_blog_id', get_current_blog_id());
+
+ $should_switch = ($page_blog_id !== get_current_blog_id());
+ if ( $should_switch ) {
+ switch_to_blog($page_blog_id);
+ }
+
+ $page = get_post($page_id);
+ $expected_post_statuses = array('publish', 'private');
+ if ( empty($page) ) {
+ printf(
+ 'Error: Page not found. Post ID %1$d does not exist on blog ID %2$d.',
+ $page_id,
+ $page_blog_id
+ );
+ } else if ( !in_array($page->post_status, $expected_post_statuses) ) {
+ printf(
+ 'Error: This page is not published. Post ID: %1$d, expected status: %2$s, actual status: "%3$s".',
+ $page_id,
+ esc_html('"' . implode('" or "', $expected_post_statuses) . '"'),
+ esc_html($page->post_status)
+ );
+ } else {
+ $heading = $item['page_heading'];
+ if ( $heading === ameMenuItem::embeddedPagePlaceholderHeading ) {
+ //Note that this means the user can't set the heading to the same text as the placeholder.
+ //That's poor design, but it probably won't matter in practice.
+ $heading = strip_tags($item['menu_title']);
+ }
+
+ echo '
+ Creating export file...
+
+ * The return value will be cast to boolean if non-boolean was returned. + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + return array_key_exists($offset, $this->screens); + } + + /** + * Offset to retrieve + * + * @link http://php.net/manual/en/arrayaccess.offsetget.php + * @param mixed $offset
+ * The offset to retrieve. + *
+ * @return mixed Can return all value types. + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->screens[$offset]; + } + + /** + * Offset to set + * + * @link http://php.net/manual/en/arrayaccess.offsetset.php + * @param mixed $offset+ * The offset to assign the value to. + *
+ * @param mixed $value+ * The value to set. + *
+ * @return void + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + $this->screens[$offset] = $value; + } + + /** + * Offset to unset + * + * @link http://php.net/manual/en/arrayaccess.offsetunset.php + * @param mixed $offset+ * The offset to unset. + *
+ * @return void + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + unset($this->screens[$offset]); + } + + #[\ReturnTypeWillChange] + public function getIterator() { + return new ArrayIterator($this->screens); + } +} + +class ameInvalidMetaBoxDataException extends RuntimeException {} \ No newline at end of file diff --git a/extras/modules/metaboxes/ameMetaBoxWrapper.php b/extras/modules/metaboxes/ameMetaBoxWrapper.php new file mode 100644 index 0000000..0949073 --- /dev/null +++ b/extras/modules/metaboxes/ameMetaBoxWrapper.php @@ -0,0 +1,106 @@ +id ) { + throw new LogicException(sprintf( + 'Meta box ID mismatch. Expected: "%s", got: "%s".', + $this->id, + $newProperties['id'] + )); + } + + $oldProperties = $this->toArray(); + $this->setProperties($newProperties); + + $changesDetected = $this->setPresence($this->hasValidCallback()); + + //Update callback file name. + if ( $this->hasValidCallback() ) { + $changesDetected = $this->updateCallbackFileName() || $changesDetected; + } + + foreach (array('title', 'context') as $key) { + if ( $oldProperties[$key] !== $newProperties[$key] ) { + $changesDetected = true; + break; + } + } + + return $changesDetected; + } + + /* + * Presence detection + */ + + public function isPresent() { + return $this->wasPresent || $this->hasValidCallback(); + } + + protected function hasValidCallback() { + return isset($this->callback) && is_callable($this->callback); + } + + public function setPresence($isPresent) { + $changed = ($this->wasPresent !== $isPresent); + $this->wasPresent = $isPresent; + return $changed; + } + + /* + * Callback file name + */ + + public function getCallbackFileName() { + //TODO: Maybe normalize this to use forward slashes always? Could help with JSON corruption caused by some DB migration plugins. + return $this->callbackFileName; + } + + private function updateCallbackFileName() { + $reflection = new AmeReflectionCallable($this->callback); + + $fileName = $reflection->getFileName(); + if ( $fileName === false ) { + $fileName = null; + } + + if ( $fileName !== $this->callbackFileName ) { + $this->callbackFileName = $fileName; + return true; //File name has changed. + } + return false; //No changes. + } + + /* + * Include additional properties on load/save. + */ + + public function toArray() { + $properties = parent::toArray(); + $properties['wasPresent'] = $this->wasPresent; + $properties['callbackFileName'] = $this->callbackFileName; + return $properties; + } + + protected function setProperties(array $properties) { + parent::setProperties($properties); + + $keysToCopy = array('wasPresent', 'callbackFileName'); + foreach ($keysToCopy as $name) { + if ( isset($properties[$name]) ) { + $this->$name = $properties[$name]; + } + } + } +} \ No newline at end of file diff --git a/extras/modules/metaboxes/amePostTypeFeature.php b/extras/modules/metaboxes/amePostTypeFeature.php new file mode 100644 index 0000000..4087d20 --- /dev/null +++ b/extras/modules/metaboxes/amePostTypeFeature.php @@ -0,0 +1,9 @@ + $unused) { + if ( !isset($this->features[$featureName]) ) { + $this->features[$featureName] = amePostTypeFeature::fromArray(array( + 'id' => $featureName, + 'title' => $this->getFeatureTitle($featureName), + )); + $changesDetected = true; + } + } + + //Remove features that no longer exist. + $existingFeatures = array_intersect_key($this->features, $currentFeatures); + if ( count($existingFeatures) !== count($this->features) ) { + $this->features = $existingFeatures; + $changesDetected = true; + } + + return $changesDetected; + } + + private function getFeatureTitle($featureName) { + if ( $featureName === 'editor' ) { + return 'Content Editor'; + } + return ucfirst($featureName); + } + + /** + * @return amePostTypeFeature[] + */ + public function getFeatures() { + return $this->features; + } + + /* + * Serialize / deserialize + */ + + public function toArray() { + return array_map(function (amePostTypeFeature $item) { + return $item->toArray(); + }, $this->features); + } + + public static function fromArray($data) { + $instance = new self(); + foreach ($data as $id => $properties) { + $instance->features[$id] = amePostTypeFeature::fromArray($properties); + } + return $instance; + } +} \ No newline at end of file diff --git a/extras/modules/metaboxes/box-refresh-template.php b/extras/modules/metaboxes/box-refresh-template.php new file mode 100644 index 0000000..ae90de9 --- /dev/null +++ b/extras/modules/metaboxes/box-refresh-template.php @@ -0,0 +1,13 @@ +
+ Refreshing available meta boxes...
+
+
+
+
+
+ This may take a few minutes. You'll be redirected to the settings screen when it's done. + If the refresh takes too long or doesn't work, please go to the screen that contains + the meta boxes that you want to hide, then go back to this page. +
\ No newline at end of file diff --git a/extras/modules/metaboxes/hide-gutenberg-panels.js b/extras/modules/metaboxes/hide-gutenberg-panels.js new file mode 100644 index 0000000..06631b7 --- /dev/null +++ b/extras/modules/metaboxes/hide-gutenberg-panels.js @@ -0,0 +1,28 @@ +'use strict'; + +( + /** + * @param {Object} data + * @param {Array} data.panelsToRemove List of Gutenberg panels to remove. + * @param {Array} data.selectorsToHide List of jQuery selectors to hide. + */ + function (data) { + if (typeof data['panelsToRemove'] !== 'undefined') { + for (var i = 0; i < data.panelsToRemove.length; i++) { + // noinspection JSUnresolvedFunction + wp.data.dispatch('core/edit-post').removeEditorPanel(data.panelsToRemove[i]); + } + } + + if (typeof data['selectorsToHide'] !== 'undefined') { + jQuery(function () { + var styleTag = jQuery(''); + var css = ''; + for (var j = 0; j < data.selectorsToHide.length; j++) { + css = css + data.selectorsToHide[j] + ' { display: none !important; }' + "\n"; + } + styleTag.text(css).appendTo('head'); + }); + } + } +)(window['wsAmeGutenbergPanelData'] || {}); \ No newline at end of file diff --git a/extras/modules/metaboxes/load.php b/extras/modules/metaboxes/load.php new file mode 100644 index 0000000..0417829 --- /dev/null +++ b/extras/modules/metaboxes/load.php @@ -0,0 +1,12 @@ + +///+ * An offset to check for. + *
+ * @return boolean true on success or false on failure. + * + *+ * The return value will be casted to boolean if non-boolean was returned. + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + return property_exists($this, $offset); + } + + /** + * Offset to retrieve + * + * @link https://php.net/manual/en/arrayaccess.offsetget.php + * @param mixed $offset
+ * The offset to retrieve. + *
+ * @return mixed Can return all value types. + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->$offset; + } + + /** + * Offset to set + * + * @link https://php.net/manual/en/arrayaccess.offsetset.php + * @param mixed $offset+ * The offset to assign the value to. + *
+ * @param mixed $value+ * The value to set. + *
+ * @return void + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + $this->$offset = $value; + } + + /** + * Offset to unset + * + * @link https://php.net/manual/en/arrayaccess.offsetunset.php + * @param mixed $offset+ * The offset to unset. + *
+ * @return void + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + $this->$offset = null; + } +} \ No newline at end of file diff --git a/extras/modules/role-editor/ameRexCapabilityDataSource.php b/extras/modules/role-editor/ameRexCapabilityDataSource.php new file mode 100644 index 0000000..714e3b5 --- /dev/null +++ b/extras/modules/role-editor/ameRexCapabilityDataSource.php @@ -0,0 +1,153 @@ +fileName = $fileName; + } + + /** + * @inheritDoc + */ + public function findCapabilities($capabilities, ameRexComponentRegistry $componentRegistry) { + $results = array(); + + /** @noinspection PhpComposerExtensionStubsInspection */ + $meta = json_decode(file_get_contents($this->fileName), true); + + foreach ($capabilities as $capability) { + if ( !isset($meta['capabilities'][$capability]) ) { + continue; + } + + $capDetails = $meta['capabilities'][$capability]; + if ( isset($capDetails['origins']) ) { + $relatedComponents = $capDetails['origins']; + } else { + $relatedComponents = $capDetails; + } + + $componentContext = array(); + foreach ($relatedComponents as $origin) { + $info = null; + if ( is_string($origin) ) { + $componentId = $origin; + } else { + $componentId = $origin['id']; + if ( count($origin) > 1 ) { + $info = ameRexComponentCapabilityInfo::fromArray($origin); + } + } + $componentContext[$componentId] = $info; + + $componentMeta = ameUtils::get($meta, 'components.' . $componentId, array()); + $componentRegistry->getOrCreate($componentId, $componentMeta); + } + + $results[$capability] = $componentContext; + } + + return $results; + } +} + +class ameRexSqliteDataSource extends ameRexCapabilityDataSource { + protected $fileName; + /** + * @var PDO|null + */ + protected $pdo = null; + + public function __construct($fileName) { + $this->fileName = $fileName; + } + + /** + * @inheritDoc + */ + public function findCapabilities($capabilities, ameRexComponentRegistry $componentRegistry) { + if ( !$this->connectToDb() ) { + return array(); + } + + $selectCap = $this->pdo->prepare(' + SELECT componentId, componentName, activeInstalls, notes, permissions, + documentationUrl, capabilityDocumentationUrl + FROM capabilityInfoView + WHERE capabilityName = :capability + '); + $selectCap->setFetchMode(PDO::FETCH_ASSOC); + + $results = array(); + foreach ($capabilities as $capability) { + $selectCap->execute(array(':capability' => $capability)); + $rows = $selectCap->fetchAll(); + $selectCap->closeCursor(); + + if ( empty($rows) ) { + continue; + } + + $componentContext = array(); + foreach ($rows as $capDetails) { + $info = null; + if ( !empty($capDetails['notes']) || !empty($capDetails['permissions']) || !empty($capDetails['documentationUrl']) ) { + if ( isset($capDetails['permissions']) ) { + //Assume one permission per line. + $capDetails['permissions'] = preg_split( + '@[\n\r]++@', + $capDetails['permissions'], + -1, + PREG_SPLIT_NO_EMPTY + ); + } + $info = ameRexComponentCapabilityInfo::fromArray($capDetails); + } + $componentContext[$capDetails['componentId']] = $info; + + $componentRegistry->updateComponent( + $capDetails['componentId'], + array( + 'name' => $capDetails['componentName'], + 'activeInstalls' => $capDetails['activeInstalls'], + 'capabilityDocumentationUrl' => $capDetails['capabilityDocumentationUrl'], + ) + ); + } + + $results[$capability] = $componentContext; + } + + return $results; + } + + protected function connectToDb() { + if ( $this->pdo ) { + return true; + } + if ( !in_array('sqlite', PDO::getAvailableDrivers()) ) { + return false; + } + + try { + $this->pdo = new PDO('sqlite:' . $this->fileName); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (PDOException $ex) { + //If the user doesn't have a SQLite driver, we can't really do anything about it. + $this->pdo = null; + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/extras/modules/role-editor/ameRexCapabilityInfoSearch.php b/extras/modules/role-editor/ameRexCapabilityInfoSearch.php new file mode 100644 index 0000000..ffed19c --- /dev/null +++ b/extras/modules/role-editor/ameRexCapabilityInfoSearch.php @@ -0,0 +1,31 @@ +sources as $source) { + $matches = $source->findCapabilities($capabilities, $componentRegistry); + foreach($matches as $capabilityName => $components) { + foreach($components as $componentId => $capInfo) { + $dataset->addResult($capabilityName, $componentId, $capInfo); + } + } + } + return $dataset; + } + + public function addDataSource(ameRexCapabilityDataSource $source) { + $this->sources[] = $source; + } +} \ No newline at end of file diff --git a/extras/modules/role-editor/ameRexCapabilitySearchResultSet.php b/extras/modules/role-editor/ameRexCapabilitySearchResultSet.php new file mode 100644 index 0000000..4439326 --- /dev/null +++ b/extras/modules/role-editor/ameRexCapabilitySearchResultSet.php @@ -0,0 +1,129 @@ +results[$capabilityName]) ) { + $this->results[$capabilityName] = array(); + } + //Only the first result per component is used. + if ( isset($this->results[$capabilityName][$componentId]) ) { + return; + } + $this->results[$capabilityName][$componentId] = $info; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + return array_key_exists($offset, $this->results); + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->results[$offset]; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + $this->results[$offset] = $value; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + unset($this->results[$offset]); + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function count() { + return count($this->results); + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function getIterator() { + return new ArrayIterator($this->results); + } +} + +class ameRexComponentCapabilityInfo implements ArrayAccess { + /** + * @var string[]|null + */ + public $permissions = null; + + /** + * @var string|null + */ + public $notes = null; + + /** + * @var string|null + */ + public $documentationUrl = null; + + public static function fromArray($properties) { + $instance = new self(); + if ( isset($properties['permissions']) ) { + $instance->permissions = $properties['permissions']; + } + if ( isset($properties['notes']) ) { + $instance->notes = $properties['notes']; + } + if ( isset($properties['documentationUrl']) ) { + $instance->documentationUrl = $properties['documentationUrl']; + } + return $instance; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + return isset($this->$offset); + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->$offset; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + $this->$offset = $value; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + $this->$offset = null; + } +} + diff --git a/extras/modules/role-editor/ameRexCategory.php b/extras/modules/role-editor/ameRexCategory.php new file mode 100644 index 0000000..d0f4fe0 --- /dev/null +++ b/extras/modules/role-editor/ameRexCategory.php @@ -0,0 +1,142 @@ +name = $name; + $this->componentId = $componentId; + } + + /** + * Does this category contain a specific capability either directly or in a subcategory? + * + * @param string $capability + * @return boolean + */ + public function hasCapability($capability) { + if (isset($this->capabilities[$capability])) { + return true; + } + foreach ($this->subcategories as $subcategory) { + if ($subcategory->hasCapability($capability)) { + return true; + } + } + return false; + } + + /** + * @param ameRexCategory $category + */ + public function addSubcategory($category) { + if (!empty($category->slug)) { + $this->subcategories[$category->slug] = $category; + } else { + $this->subcategories[] = $category; + } + } + + /** + * @param string $capability + */ + public function addCapabilityToDefaultLocation($capability) { + //TODO: Handle the situation where the default is a "General" subcategory or something like that. + $this->capabilities[$capability] = true; + } + + public function toArray() { + $result = array( + 'name' => $this->name, + 'componentId' => $this->componentId, + 'capabilities' => array_keys($this->capabilities), + ); + if (!empty($this->subcategories)) { + $result['subcategories'] = array(); + foreach ($this->subcategories as $subcategory) { + $result['subcategories'][] = $subcategory->toArray(); + } + } + if (($this->slug !== '') && ($this->slug !== null)) { + $result['slug'] = $this->slug; + } + return $result; + } +} + +abstract class ameRexExtendedCategory extends ameRexCategory { + protected $variant = null; + protected $contentTypeId = null; + /** + * @var string[] + */ + public $permissions = array(); + + public function __construct($name = '', $componentId = null, $contentTypeId = null, $permissions = array()) { + parent::__construct($name, $componentId); + $this->permissions = $permissions; + $this->contentTypeId = $contentTypeId; + } + + public function hasCapability($capability) { + if (parent::hasCapability($capability)) { + return true; + } + + /* + * Check if the capability is used by one of the permissions. + * + * Implementation note: This code does not use in_array() because, in loose + * comparison mode, it will produce crazy results like treating "abc" and + * the boolean TRUE as equal. Unfortunately, strict mode also does not work + * here because some capabilities can be represented as either numbers or + * numeric strings. PHP converts numeric strings to numbers when WordPress + * uses them as array indexes. + * + * Instead, we use a standard loop with a strict comparison, plus a special + * case for numeric capabilities. + */ + + $isNeedleNumeric = is_numeric($capability); + $needleAsString = (string)$capability; + + foreach ($this->permissions as $capName) { + if ( + //Strict equality check first. + ($capName === $capability) + //Relaxed comparison for numbers. + || ($isNeedleNumeric && ($needleAsString === (string)$capName)) + ) { + return true; + } + } + + return false; + } + + public function toArray() { + $result = parent::toArray(); + $result['variant'] = $this->variant; + $result['permissions'] = $this->permissions; + $result['contentTypeId'] = $this->contentTypeId; + unset($result['capabilities']); + return $result; + } +} + + +class ameRexPostTypeCategory extends ameRexExtendedCategory { + protected $variant = 'post_type'; +} + +class ameRexTaxonomyCategory extends ameRexExtendedCategory { + protected $variant = 'taxonomy'; +} \ No newline at end of file diff --git a/extras/modules/role-editor/ameRexComponent.php b/extras/modules/role-editor/ameRexComponent.php new file mode 100644 index 0000000..473c216 --- /dev/null +++ b/extras/modules/role-editor/ameRexComponent.php @@ -0,0 +1,48 @@ +id = $id; + $this->name = ($name !== null) ? $name : $id; + } + + public function toArray() { + $result = array( + 'componentId' => $this->id, + 'name' => $this->name, + 'activeInstalls' => $this->activeInstalls, + 'isActive' => $this->isActive, + 'isInstalled' => $this->isInstalled, + ); + + if ($this->capabilityDocumentationUrl) { + $result['capabilityDocumentationUrl'] = $this->capabilityDocumentationUrl; + } + + return $result; + } +} \ No newline at end of file diff --git a/extras/modules/role-editor/ameRexComponentRegistry.php b/extras/modules/role-editor/ameRexComponentRegistry.php new file mode 100644 index 0000000..2ddbe42 --- /dev/null +++ b/extras/modules/role-editor/ameRexComponentRegistry.php @@ -0,0 +1,95 @@ +components[$component->id] = $component; + } + + public function get($componentId) { + if ( isset($this->components[$componentId]) ) { + return $this->components[$componentId]; + } + return null; + } + + public function getOrCreate($componentId, $properties = null) { + if ( isset($this->components[$componentId]) ) { + return $this->components[$componentId]; + } + if ( $properties !== null ) { + $component = new ameRexComponent($componentId, isset($properties['name']) ? $properties['name'] : null); + if ( isset($properties['activeInstalls']) ) { + $component->activeInstalls = $properties['activeInstalls']; + } + if ( isset($properties['capabilityDocumentationUrl']) ) { + $component->capabilityDocumentationUrl = $properties['capabilityDocumentationUrl']; + } + $this->register($component); + return $component; + } + return null; + } + + /** + * @param string $componentId + * @param array $properties + * @return ameRexComponent + */ + public function updateComponent($componentId, $properties) { + if ( isset($this->components[$componentId]) ) { + $component = $this->components[$componentId]; + } else { + $component = new ameRexComponent($componentId, isset($properties['name']) ? $properties['name'] : null); + $this->register($component); + } + if ( isset($properties['activeInstalls']) ) { + $component->activeInstalls = $properties['activeInstalls']; + } + if ( isset($properties['capabilityDocumentationUrl']) ) { + $component->capabilityDocumentationUrl = $properties['capabilityDocumentationUrl']; + } + return $component; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function getIterator() { + return new ArrayIterator($this->components); + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + return array_key_exists($offset, $this->components); + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->components[$offset]; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + $this->components[$offset] = $value; + } + + /** + * @inheritDoc + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + unset($this->components[$offset]); + } +} \ No newline at end of file diff --git a/extras/modules/role-editor/ameRexSettingsValidator.php b/extras/modules/role-editor/ameRexSettingsValidator.php new file mode 100644 index 0000000..fabd47f --- /dev/null +++ b/extras/modules/role-editor/ameRexSettingsValidator.php @@ -0,0 +1,686 @@ +inputData = $jsonString; + $this->preExistingCapabilities = $allCapabilities; + $this->preExistingUserDefinedCaps = $userDefinedCaps; + $this->oldEditableRoleSettings = $oldEditableRoleSettings; + $this->effectiveEditableRoles = $effectiveEditableRoles; + $this->menuEditor = $menuEditor; + } + + public function validate() { + $this->errors = array(); + $data = json_decode($this->inputData, true); + + if ($data === null) { + $this->errors[] = new WP_Error( + 'ame_rex_invalid_json', + 'JSON parsing failed. Submitted settings are probably invalid or corrupted.' + ); + return $this->errors; + } + + if (!is_array($data)) { + $this->errors[] = new WP_Error( + 'ame_rex_unexpected_data_type', + sprintf('JSON parsing failed. Expected type: associative array, actual type: %s.', gettype($data)) + ); + return $this->errors; + } + + $submittedRoles = array(); + foreach ($data['roles'] as $tempRole) { + $submittedRoles[$tempRole['name']] = $tempRole; + } + + $wpRoles = ameRoleUtils::get_roles();; + $existingRoles = (isset($wpRoles->roles) && is_array($wpRoles->roles)) ? $wpRoles->roles : array(); + + $knownRoleIDs = array_fill_keys( + array_merge(array_keys($existingRoles), array_keys($submittedRoles)), + true + ); + + $editableRoles = $this->effectiveEditableRoles; + + //For validation purposes, we also need existing capabilities. + $existingCapabilities = $this->preExistingCapabilities; + + //Remove all roles that don't exist in the role list. + $rolesToDelete = array_diff_key($editableRoles, $submittedRoles); + //Don't delete the default role. The user should set a different default role first. + $defaultRole = get_option('default_role'); + if (isset($rolesToDelete[$defaultRole])) { + unset($rolesToDelete[$defaultRole]); + $this->errors[] = new WP_Error( + 'ame_rex_cannot_delete_default_role', + 'You cannot delete the default role. Set a different default role first.' + ); + } + //Don't delete roles that are currently assigned to one or more users. This check may be slow. + if (count($rolesToDelete) > 0) { + $usersByRole = count_users(); + if (isset($usersByRole['avail_roles'])) { + foreach ($usersByRole['avail_roles'] as $id => $totalUsers) { + if (($totalUsers > 0) && isset($rolesToDelete[$id])) { + unset($rolesToDelete[$id]); + $this->errors[] = new WP_Error( + 'ame_rex_deleted_role_has_users', + sprintf( + 'Role "%s" cannot be deleted because there are still %d users with that role.', + $id, + $totalUsers + ) + ); + } + } + } + } + + $rolesToCreate = array(); + $rolesToModify = array(); + + //Validate all new or modified properties. + foreach ($submittedRoles as $id => $role) { + $isNewRole = !isset($existingRoles[$id]); + $isModifiedRole = false; + + //Only modify existing roles if they're editable. + if (!$isNewRole && (!isset($editableRoles[$id]) || !isset($wpRoles->role_objects[$id]))) { + $this->errors[] = new WP_Error( + 'ame_rex_role_not_editable', + sprintf('You don\'t have permission to edit role "%s"', $id) + ); + continue; + } + + //Validate the role ID (internal name). + if ($isNewRole) { + $state = $this->validateRoleName($id, $existingRoles, $existingCapabilities); + if (is_wp_error($state)) { + $this->errors[] = $state; + continue; + } + } + + //Validate the display name. + if ($isNewRole || ($editableRoles[$id]['name'] !== $role['displayName'])) { + $state = $this->validateRoleDisplayName($role['displayName']); + if (is_wp_error($state)) { + $this->errors[] = $state; + continue; + } + $isModifiedRole = $isModifiedRole || !$isNewRole; + } + + //Validate capabilities. + $capErrors = $this->validateCapabilityAssignment( + $role['capabilities'], + $existingCapabilities, + $knownRoleIDs + ); + if (!empty($capErrors)) { + $this->errors = array_merge($this->errors, $capErrors); + continue; + } + + if (!$isNewRole) { + //Have any of the capabilities changed? + $oldCaps = $this->menuEditor->castValuesToBool($wpRoles->roles[$id]['capabilities']); + if (!$this->areAssocArraysEqual($oldCaps, $role['capabilities'])) { + $isModifiedRole = true; + } + } + + //Everything looks valid. + if ($isNewRole) { + $rolesToCreate[$id] = $role; + } else if ($isModifiedRole) { + $rolesToModify[$id] = $role; + } + } + + //TODO: Existing roles might have to include newly added roles. + + //Validate user settings. + //----------------------- + + $submittedUsers = ameUtils::get($data, 'users', array()); + if (!is_array($submittedUsers)) { + $submittedUsers = array(); + } + $existingUsers = array(); + $usersToModify = array(); + foreach ($submittedUsers as $modifiedUser) { + //Skip malformed user records that are missing required fields. + if (!isset($modifiedUser, $modifiedUser['userId'], $modifiedUser['capabilities'], $modifiedUser['roles'])) { + continue; + } + $userId = intval(ameUtils::get($modifiedUser, 'userId', 0)); + + //User must exist. + $user = get_user_by('id', $userId); + if (empty($user) || !$user->exists()) { + continue; + } + + $previousRoles = array(); + if (isset($user->roles) && is_array($user->roles)) { + $previousRoles = array_values($user->roles); + } + $previousCapsWithoutRoles = array_diff_key( + (isset($user->caps) && is_array($user->caps)) ? $user->caps : array(), + array_fill_keys($previousRoles, true) + ); + + //TODO: Allow adding newly created roles if they are editable. Tricky. Might be better to disable that option in the editor. + //Validate roles. + list($newRoles, $roleErrors) = $this->validateUserRoleChange( + $previousRoles, + $modifiedUser['roles'], + $editableRoles + ); + $modifiedUser['roles'] = $newRoles; + + //Validate capabilities. + $newCapsWithoutRoles = array_diff_key( + $modifiedUser['capabilities'], + $knownRoleIDs, + array_fill_keys($newRoles, true) + ); + $capErrors = $this->validateCapabilityAssignment( + $newCapsWithoutRoles, + $existingCapabilities, + $knownRoleIDs + ); + $modifiedUser['capabilities'] = $newCapsWithoutRoles; + + //Have any of the roles or capabilities actually changed? + $isModifiedUser = false; + $oldCaps = $this->menuEditor->castValuesToBool($previousCapsWithoutRoles); + if (!$this->areAssocArraysEqual($oldCaps, $newCapsWithoutRoles)) { + $isModifiedUser = true; + } + //Note: The order of roles is significant. + if (!$this->areAssocArraysEqual($previousRoles, $newRoles)) { + $isModifiedUser = true; + } + + //Don't check permissions if the user hasn't actually made any changes. + if (!$isModifiedUser) { + continue; + } + + //The current user must have permission to edit this user. + if (!current_user_can('edit_user', $userId)) { + $this->errors[] = new WP_Error( + 'ame_uneditable_user', + sprintf('You don\'t have sufficient permissions to edit the user with ID #%d.', $userId) + ); + continue; + } + + //Now validation errors actually matter. + if (!empty($capErrors)) { + $this->errors = array_merge($this->errors, $capErrors); + continue; + } + if (!empty($roleErrors)) { + $this->errors = array_merge($this->errors, $roleErrors); + continue; + } + + if ($isModifiedUser) { + $usersToModify[$userId] = $modifiedUser; + $existingUsers[$userId] = $user; + } + } + + //Now that we know what roles exist, we can validate and save user-defined capabilities. + $this->userDefinedCaps = null; + if (isset($data['userDefinedCaps']) && is_array($data['userDefinedCaps'])) { + $validCaps = array(); + foreach ($data['userDefinedCaps'] as $capability) { + $status = $this->validateCapabilityName($capability, $knownRoleIDs); + if (!is_wp_error($status)) { + $validCaps[$capability] = true; + } + } + $this->userDefinedCaps = $validCaps; + + $addedCaps = array_diff_key($this->userDefinedCaps, $this->preExistingUserDefinedCaps); + $deletedCaps = array_diff_key($this->preExistingUserDefinedCaps, $this->userDefinedCaps); + $this->modifiedUserDefinedCapCount = count($addedCaps) + count($deletedCaps); + } + + //Validate "editable roles" settings. + $submittedEditableRoles = ameUtils::get($data, 'editableRoles', array()); + if (!is_array($submittedEditableRoles)) { + $submittedEditableRoles = array(); + } + $allowedStrategies = array('auto', 'none', 'user-defined-list'); + $newEditableRoles = array(); + foreach($submittedEditableRoles as $actorId => $settings) { + if (!$this->userCanEditActor($actorId, $editableRoles)) { + if (isset($this->oldEditableRoleSettings[$actorId])) { + $newEditableRoles[$actorId] = $this->oldEditableRoleSettings[$actorId]; + } + continue; + } + + //Validate the strategy. + if (!isset($settings['strategy']) || !in_array($settings['strategy'], $allowedStrategies)) { + $settings['strategy'] = 'auto'; + } + //The user-defined role list, if any, must be in the form of [roleId => true]. + if ($settings['strategy'] === 'user-defined-list') { + $sanitizedList = array(); + if (is_array($settings['userDefinedList'])) { + foreach(array_keys($settings['userDefinedList']) as $roleId) { + $sanitizedList[strval($roleId)] = true; + } + } + $settings['userDefinedList'] = $sanitizedList; + } else { + $settings['userDefinedList'] = null; + } + + //"auto" is the default so we don't need to store it. + if ($settings['strategy'] === 'auto') { + $settings = null; + } + + $newEditableRoles[$actorId] = $settings; + } + //Restore removed settings if the user doesn't have permission to edit them. + $removedSettings = array_diff_key($this->oldEditableRoleSettings, $submittedEditableRoles); + foreach($removedSettings as $actorId => $settings) { + if (!$this->userCanEditActor($actorId, $editableRoles) && isset($this->oldEditableRoleSettings[$actorId]) ) { + $newEditableRoles[$actorId] = $this->oldEditableRoleSettings[$actorId]; + } + } + //Check if we have actually made any changes. + if (!$this->areAssocArraysRecursivelyEqual($newEditableRoles, $this->oldEditableRoleSettings)) { + $this->areEditableRolesModified = true; + } + + $this->rolesToModify = $rolesToModify; + $this->rolesToCreate = $rolesToCreate; + $this->rolesToDelete = $rolesToDelete; + $this->usersToModify = $usersToModify; + $this->knownRoleIDs = $knownRoleIDs; + $this->existingUsers = $existingUsers; + $this->editableRoleSettings = $newEditableRoles; + + return $this->errors; + } + + /** + * @param string $name + * @param array $roles + * @param array $capabilities + * @return bool|WP_Error + */ + private function validateRoleName($name, $roles = array(), $capabilities = array()) { + $name = trim($name); + + if ($name === '') { + return new WP_Error('ame_empty_role_name', 'Role name cannot be empty.'); + } + + //Name can only contain certain characters. + if (preg_match('/[^a-z0-9_]/', $name)) { + return new WP_Error( + 'ame_invalid_characters_in_name', + 'Role name contains invalid characters. Please use only lowercase English letters, numbers, and underscores.' + ); + } + + //Numeric names could cause problems with how PHP handles associative arrays. + if (is_numeric($name)) { + return new WP_Error('ame_numeric_role_name', 'Numeric role names are not allowed.'); + } + + //Name must not be a duplicate. + if (array_key_exists($name, $roles)) { + return new WP_Error('ame_duplicate_role', 'Duplicate role name.'); + } + + //WP stores capabilities and role names in the same associative array, + //so they must be unique with respect to each other. + if (array_key_exists($name, $capabilities)) { + return new WP_Error('ame_role_matches_capability', 'Role name can\'t be the same as a capability name.'); + } + + return true; + } + + /** + * @param string $displayName + * @return bool|WP_Error + */ + private function validateRoleDisplayName($displayName) { + $displayName = trim($displayName); + + if ($displayName === '') { + return new WP_Error('ame_empty_role_display_name', 'Role display name cannot be empty.'); + } + + if (preg_match('/[><&\r\n\t]/', $displayName)) { + return new WP_Error('ame_invalid_display_name_chars', 'Role display name contains invalid characters.'); + } + + return true; + } + + /** + * @param string $capability + * @param string[] $roles + * @return bool|WP_Error + */ + private function validateCapabilityName($capability, $roles = array()) { + if ($capability === '') { + return new WP_Error('ame_empty_cap', 'Capability name must not be an empty string.'); + } + + //WP API allows completely arbitrary capability names, but this plugin forbids some characters + //for sanity's sake and to avoid XSS. + static $invalidCharacters = '/[><&\r\n\t]/'; + if (preg_match($invalidCharacters, $capability)) { + return new WP_Error('ame_invalid_cap_characters', 'Capability name contains invalid characters.'); + } + + //PHP doesn't allow numeric string keys, and there's no conceivable reason to start the name with a space. + static $invalidFirstCharacter = '/^[\s0-9]/i'; + if (preg_match($invalidFirstCharacter, $capability)) { + return new WP_Error('ame_invalid_cap_start', 'Capability name cannot start with a number or a space.'); + } + + //Roles and caps are stored in the same array, so they must be mutually unique. + if (array_key_exists($capability, $roles)) { + return new WP_Error( + 'ame_cap_equals_role', + sprintf('Capability name "%s" cannot be the same as the name of a role.', $capability) + ); + } + + //Some capabilities are special and should never be directly assigned to roles. + static $excludedCaps = array('do_not_allow', 'exist', 'customize'); + if (in_array($capability, $excludedCaps)) { + return new WP_Error( + 'ame_create_reserved_cap', + 'Cannot create a capability that matches a meta capability or a reserved capability.' + ); + } + + return true; + } + + private function validateCapabilityAssignment($capabilities, $existingCapabilities, $roles = array()) { + //Preexisting capabilities can be granted even if they don't meet our validation requirements. + $newCaps = array_diff_key($capabilities, $existingCapabilities); + $errors = array(); + foreach ($newCaps as $capability => $isGranted) { + $validationState = $this->validateCapabilityName($capability, $roles); + if (is_wp_error($validationState)) { + $errors[] = $validationState; + } + } + return $errors; + } + + /** + * Verify that the current user has permission to change a user's roles from $oldRoles to $newRoles. + * + * Returns the roles that the selected user should have after the change. Any invalid changes - like adding + * or removing non-editable roles - will be undone. + * + * For example, lets say that the selected user has these roles: + * $oldRoles = ['administrator', 'foo', 'bar'] + * + * Then the current user tries to change their roles to this: + * $newRoles = ['foo', 'author', 'qux'] + * + * Lets assume that the current user can edit all roles except "administrator", "foo" and "qux". + * Here's what the function will return: + * ['administrator', 'foo', 'author'] + * + * Here's what it does: + * - Prevent the attempt to remove a non-editable role ('administrator'). + * - Prevent the attempt to add a non-editable role ('qux'). + * - Keep 'administrator' as the primary role because it's not editable. + * - Let the user add or remove any roles that they can edit ('author', 'bar'). + * - let the user include roles that they can't edit if the subject already had them ('foo'). + * + * @param array $oldRoles Current role IDs. Example: array('administrator', 'foo', 'bar'). + * @param array $newRoles New role IDs. Example: array('foo', 'author', 'qux'). + * @param array|null $editableRoles + * @return array [validated-new-roles, errors] + */ + private function validateUserRoleChange($oldRoles, $newRoles, $editableRoles = null) { + if ($editableRoles === null) { + $editableRoles = get_editable_roles(); + if (!is_array($editableRoles)) { + $editableRoles = array(); + } + } + $errors = array(); + + if (!is_array($newRoles)) { + return array($oldRoles, array(new WP_Error('ame_rex_invalid_argument', 'Role list must be an array.'))); + } + + $newPrimaryRole = reset($newRoles); + + //NB: It is NOT an error to select a new primary role when the old one is not editable. + //WordPress UI simply doesn't give the option to leave the role unchanged. We shouldn't penalize users for that. + $oldPrimaryRole = reset($oldRoles); + if (is_string($oldPrimaryRole) && ($oldPrimaryRole !== '') && !isset($editableRoles[$oldPrimaryRole])) { + //Keep the existing primary role. Treat the new one as a normal "other" role. + $newPrimaryRole = $oldPrimaryRole; + array_unshift($newRoles, $oldPrimaryRole); //This might duplicate the role. We'll remove duplicates later. + } + + //It's always valid to keep the same roles, even if the current user can't edit them. + $validNewRoles = array_intersect($newRoles, $oldRoles); + + //Does the current user have permission to add/remove these roles? + $changedRoles = array_merge( + array_fill_keys(array_diff($newRoles, $oldRoles), 'add'), + array_fill_keys(array_diff($oldRoles, $newRoles), 'remove') + ); + $errorMessages = array( + 'add' => 'You cannot give users the "%s" role.', + 'remove' => 'You cannot remove the "%s" role from users.', + ); + + foreach ($changedRoles as $roleId => $action) { + $isAllowed = isset($editableRoles[$roleId]); + + if (($isAllowed && ($action === 'add')) || (!$isAllowed && ($action === 'remove'))) { + $validNewRoles[] = $roleId; + } + + if (!$isAllowed && isset($errors)) { + $errors[] = new WP_Error( + sprintf('ame_rex_cannot_%1$s_role_%2$s', $action, $roleId), + sprintf($errorMessages[$action], htmlentities($roleId)) + ); + } + } + + //Move the primary role to the start of the array. + $primaryRoleIndex = array_search($newPrimaryRole, $validNewRoles, true); + if ($newPrimaryRole && ($primaryRoleIndex > 0)) { + unset($validNewRoles[$primaryRoleIndex]); + array_unshift($validNewRoles, $newPrimaryRole); + } + + //Deduplicate roles. array_unique() sorts the array but preserves keys, so we can use ksort() to restore order. + $validNewRoles = array_unique($validNewRoles, SORT_STRING); //Requires PHP >= 5.2.9 + ksort($validNewRoles); + + return array(array_values($validNewRoles), $errors); + } + + /** + * Check if the currently logged-in user can edit the settings of a specific actor. + * + * @param string $actorId + * @param array $currentEditableRoles + * @return bool + */ + private function userCanEditActor($actorId, $currentEditableRoles) { + if ($actorId === 'special:super_admin') { + return is_super_admin(); + } + + list($type, $name) = explode(':', $actorId, 2); + if ($type === 'user') { + $victim = get_user_by('login', $name); + if ($victim) { + return current_user_can('edit_user', $victim->ID); + } + return current_user_can('edit_users'); + } + + if ($type === 'role') { + return isset($currentEditableRoles[$name]); + } + + return false; + } + + /** + * Check if two arrays have the same keys and values. Arrays with string keys + * or mixed keys can be in different order and still be considered "equal". + * + * @param array $a + * @param array $b + * @return bool + */ + private function areAssocArraysEqual($a, $b) { + if (count($a) !== count($b)) { + return false; + } + $sameItems = array_intersect_assoc($a, $b); + return count($sameItems) === count($b); + } + + /** + * Like areAssocArraysEqual(), but also compares nested arrays. + * + * @param array $a + * @param array $b + * @return bool + */ + private function areAssocArraysRecursivelyEqual($a, $b) { + if (count($a) !== count($b)) { + return false; + } + $sameKeys = array_intersect_key($a, $b); + if (count($sameKeys) !== count($a)) { + return false; + } + foreach($sameKeys as $key => $valueA) { + $valueB = $b[$key]; + if ($valueA !== $valueB) { + if (is_array($valueA) && is_array($valueB)) { + if (!$this->areAssocArraysRecursivelyEqual($valueA, $valueB)) { + return false; + } + } else { + return false; + } + } + } + return true; + } + + public function getUserDefinedCaps() { + return $this->userDefinedCaps; + } + + public function getTotalChangeCount() { + //TODO: Maybe count each modified capability as a separate change. + return count($this->rolesToDelete) + count($this->rolesToCreate) + + count($this->rolesToModify) + count($this->usersToModify) + + $this->modifiedUserDefinedCapCount + ($this->areEditableRolesModified ? 1 : 0); + } + + public function getRolesToDelete() { + return $this->rolesToDelete; + } + + public function getRolesToModify() { + return $this->rolesToModify; + } + + public function getRolesToCreate() { + return $this->rolesToCreate; + } + + public function getUsersToModify() { + return $this->usersToModify; + } + + public function getNewEditableRoleSettings() { + return $this->editableRoleSettings; + } + + /** + * @param $userId + * @return WP_User + */ + public function getExistingUser($userId) { + return $this->existingUsers[$userId]; + } +} \ No newline at end of file diff --git a/extras/modules/role-editor/ameRoleEditor.php b/extras/modules/role-editor/ameRoleEditor.php new file mode 100644 index 0000000..a4b7d5b --- /dev/null +++ b/extras/modules/role-editor/ameRoleEditor.php @@ -0,0 +1,2061 @@ + $filterInstance] + */ + private $cachedEditableRoles = array(); + /** + * @var string[] Overall, most specific strategy per user. [123 => 'auto', 456 => 'user-defined-list', ...] + */ + private $cachedOverallEditableRoleStrategy = array(); + /** + * @var bool Is the hook that clears the role cache already installed? + */ + private $isRoleCacheClearingHookSet = false; + /** + * @var array + */ + private $cachedEnabledRoleCaps = array(); + + public function __construct($menuEditor) { + parent::__construct($menuEditor); + + add_filter('editable_roles', array($this, 'filterEditableRoles'), 20, 1); + add_filter('map_meta_cap', array($this, 'restrictUserEditing'), 10, 4); + + //Optimization: Only record plugins that register post types and taxonomies when the current page is an AME tab. + if (isset($_GET['sub_section'])) { + add_action('registered_post_type', array($this, 'recordPostTypeOrigin'), 10, 2); + add_action('registered_taxonomy', array($this, 'recordTaxonomyOrigin'), 10, 3); + } + + add_action('wp_ajax_' . self::UPDATE_PREFERENCES_ACTION, array($this, 'ajaxUpdateUserPreferences')); + + /** @var wpdb */ + global $wpdb; + $this->backupTable = $wpdb->base_prefix . 'ame_role_backups'; + add_action(self::BACKUP_CLEANUP_HOOK, array($this, 'deleteOldRoleBackups')); + } + + public function enqueueTabScripts() { + parent::enqueueTabScripts(); + + wp_register_auto_versioned_script( + 'ame-role-editor', + plugins_url('role-editor.js', __FILE__), + array( + 'ame-lodash', + 'knockout', + 'jquery', + 'jquery-qtip', + 'ame-actor-manager', + 'ame-actor-selector', + 'ame-ko-extensions', + ) + ); + + wp_enqueue_script('ame-role-editor'); + + $this->knownComponents = new ameRexComponentRegistry(); + $this->queryInstalledComponents(); + + $defaultCapabilities = $this->getDefaultCapabilities(); + $multisiteCapabilities = $this->getMultisiteOnlyCapabilities(); + + foreach ($this->getAllCapabilities(array(wp_get_current_user())) as $capability => $unusedValue) { + $descriptor = new ameRexCapability(); + if (isset($defaultCapabilities[$capability]) || isset($multisiteCapabilities[$capability])) { + $descriptor->componentId = self::CORE_COMPONENT_ID; + } else { + $this->uncategorizedCapabilities[$capability] = true; + } + $this->capabilities[$capability] = $descriptor; + } + //TODO: do_not_allow should never end up in a plugin category. It's part of core. + + $postTypes = $this->getPostTypeDescriptors(); + $this->analysePostTypes($postTypes); + + $taxonomies = $this->findRegisteredTaxonomies(); + $this->analyseTaxonomies($taxonomies); + + //$categorizationStartTime = microtime(true); + + //Check which menu items use what capabilities and what the corresponding components are. + $this->analyseAdminMenuCapabilities(); + + $this->queryCapabilityDatabase(); + + $this->assignCapabilitiesToComponents(); + + $probablePostTypeCategories = $this->findProbablePostTypeCategories(); + $clusteredCategories = $this->groupSimilarCapabilities(); + + foreach ($this->componentRootCategories as $category) { + //Find component roots that have both subcategories and capabilities and + //put all freestanding capabilities in a "General" subcategory. + if (!empty($category->capabilities) && !empty($category->subcategories)) { + $generalCategory = new ameRexCategory('General', $category->componentId); + $generalCategory->capabilities = $category->capabilities; + $category->capabilities = array(); + array_unshift($category->subcategories, $generalCategory); + } + } + + $coreCategory = $this->loadCoreCategories(); + + //Normally, only a Super Admin on Multisite has certain Multisite administration capabilities. + //However, there is at least one plugin that uses these capabilities even in a regular WP install, + //so we'll show them as long as they're assigned to at least one role or user. + if (!is_multisite() && isset($coreCategory->subcategories['default/multisite'])) { + $multisiteCategory = $coreCategory->subcategories['default/multisite']; + $multisiteCategory->capabilities = array_intersect_key($multisiteCategory->capabilities, $this->capabilities); + if (empty($multisiteCategory->capabilities)) { + unset($coreCategory->subcategories['default/multisite']); + } + } + + /*echo ''; + print_r($clusteredCategories); + print_r($probablePostTypeCategories); + print_r(array_keys($this->uncategorizedCapabilities)); + print_r($this->capabilities); + exit;*/ + + //$elapsed = microtime(true) - $categorizationStartTime; + //printf('Categorization time: %.3f ms', $elapsed * 1000); + //exit; + + $customCategories = array_merge($this->componentRootCategories, $probablePostTypeCategories, $clusteredCategories); + $customCategoryDescriptors = array(); + foreach ($customCategories as $category) { + /** @var ameRexCategory $category */ + $customCategoryDescriptors[] = $category->toArray(); + } + + $components = array(); + foreach ($this->knownComponents as $id => $component) { + $components[$id] = $component->toArray(); + } + + $stableMetaCaps = self::loadCapabilities('stable-meta-caps.txt'); + $metaCapMap = array(); + $currentUserId = get_current_user_id(); + foreach ($stableMetaCaps as $metaCap => $unused) { + $primitiveCaps = map_meta_cap($metaCap, $currentUserId); + if ((count($primitiveCaps) === 1) && !in_array('do_not_allow', $primitiveCaps)) { + $targetCap = reset($primitiveCaps); + if ($targetCap !== $metaCap) { + $metaCapMap[$metaCap] = $targetCap; + } + } + } + + $userPreferences = array(); + $userPreferenceData = get_user_meta(get_current_user_id(), self::USER_PREFERENCE_KEY, true); + if (is_string($userPreferenceData) && !empty($userPreferenceData)) { + $userPreferences = json_decode($userPreferenceData, true); + if (!is_array($userPreferences)) { + $userPreferences = array(); + } + } + + $query = $this->menuEditor->get_query_params(); + $selectedActor = null; + if (isset($query['selected_actor'])) { + $selectedActor = strval($query['selected_actor']); + } + + $scriptData = array( + 'coreCategory' => $coreCategory->toArray(), + 'customCategories' => $customCategoryDescriptors, + 'postTypes' => $postTypes, + 'taxonomies' => $taxonomies, + 'capabilities' => $this->capabilities, + 'uncategorizedCapabilities' => array_keys($this->uncategorizedCapabilities), + 'deprecatedCapabilities' => self::loadCapabilities('deprecated-capabilities.txt'), + 'userDefinedCapabilities' => $this->getUserDefinedCaps(), + 'knownComponents' => $components, + 'metaCapMap' => $metaCapMap, + 'roles' => $this->getRoleData(), + 'users' => array(), + 'defaultRoleName' => get_option('default_role'), + 'trashedRoles' => array(), //todo: Load trashed roles from somewhere. + 'selectedActor' => $selectedActor, + 'editableRoles' => ameUtils::get($this->loadSettings(), 'editableRoles', new stdClass()), + + 'userPreferences' => $userPreferences, + 'adminAjaxUrl' => self_admin_url('admin-ajax.php'), + 'updatePreferencesNonce' => wp_create_nonce(self::UPDATE_PREFERENCES_ACTION), + ); + + $jsonData = wp_json_encode($scriptData); + + if ( !is_string($jsonData) ) { + $message = sprintf( + 'Failed to encode role data as JSON. The encoding function returned a %s.', + esc_html(gettype($jsonData)) + ); + if ( function_exists('json_last_error_msg') ) { + $message .= sprintf( + '
JSON error message: "%s".', + esc_html(json_last_error_msg()) + ); + } + if ( function_exists('json_last_error') ) { + $message .= sprintf(' JSON error code: %d.', json_last_error()); + } + + add_action('all_admin_notices', function () use ($message, $scriptData) { + printf('', $message); + }); + } + + wp_add_inline_script( + 'ame-role-editor', + sprintf('wsRexRoleEditorData = (%s);', $jsonData) + ); + } + + public function enqueueTabStyles() { + parent::enqueueTabStyles(); + wp_enqueue_auto_versioned_style( + 'ame-role-editor-styles', + plugins_url('role-editor.css', __FILE__) + ); + } + + public function displaySettingsPage() { + if (!$this->userCanAccessModule()) { + echo 'Error: You don\'t have sufficient permissions to access these settings.'; + return; + } + + if ($this->backupsEnabled && !wp_next_scheduled(self::BACKUP_CLEANUP_HOOK)) { + wp_schedule_event(time() + 10 * 60, 'daily', self::BACKUP_CLEANUP_HOOK); + } + parent::displaySettingsPage(); + } + + private function getPostTypeDescriptors() { + $results = array(); + $wpPostTypes = get_post_types(array(), 'objects'); + + //Note: When the "map_meta_cap" option is disabled for a CPT, the values of the "cap" + //object will be treated as primitive capabilities. For example, "read_post" => "read_post" + //means that WP will actually check if the user has the literal "read_post" capability. + + //On the other hand, when "map_meta_cap" is enabled, some "cap" entries can be re-mapped + //to other "cap" entries depending on the current user, post owner, post status, etc. + + //These three meta capabilities will always be mapped to something else if "map_meta_cap" + //is enabled. We'll skip them unless mapping is off or someone has assigned them to a role. + //Note: It's possible that it would be fine to skip them even then. Not sure. + $metaCaps = array('edit_post', 'read_post', 'delete_post'); + + foreach ($wpPostTypes as $name => $postType) { + $isIncluded = $postType->public || !$postType->_builtin; + + //Skip the "attachment" post type. It only has one unique capability (upload_files), which + //is included in a default group. + if ($name === 'attachment') { + $isIncluded = false; + } + + if (!$isIncluded) { + continue; + } + + $label = $name; + $pluralLabel = $name; + if (isset($postType->labels, $postType->labels->name) && !empty($postType->labels->name)) { + $label = $postType->labels->name; + $pluralLabel = $postType->labels->name; + } + + //We want the plural in lowercase, but if there are multiple consecutive uppercase letters + //then it's probably an acronym. Stuff like "aBc" is probably a contraction or a proper noun. + if (!preg_match('@([A-Z]{2}|[a-z][A-Z])@', $pluralLabel)) { + $pluralLabel = strtolower($pluralLabel); + } + + $capabilities = array(); + foreach ((array)$postType->cap as $capType => $capability) { + //Skip meta caps unless they already exist. + if (in_array($capType, $metaCaps) && ($postType->map_meta_cap || !isset($this->capabilities[$capability]))) { + continue; + } + + //Skip the "read" cap. It's redundant - most CPTs use it, and all roles have it by default. + if (($capType === 'read') && ($capability === 'read')) { + continue; + } + + //Some plugins apparently set capability to "false". Perhaps the intention is to disable it. + if ($capability === false) { + continue; + } + + $capabilities[$capType] = $capability; + } + + $component = isset($this->postTypeRegistrants[$name]) ? $this->postTypeRegistrants[$name] : null; + + $descriptor = array( + 'label' => $label, + 'name' => $name, + 'pluralLabel' => $pluralLabel, + 'permissions' => $capabilities, + 'isDefault' => isset($postType->_builtin) && $postType->_builtin, + 'componentId' => $component, + ); + + $results[$name] = $descriptor; + } + + return $results; + } + + protected function findRegisteredTaxonomies() { + $registeredTaxonomies = array(); + $usedLabels = array('Categories' => true, 'Category' => true, 'Tags' => true); + + foreach (get_taxonomies(array(), 'object') as $taxonomy) { + $permissions = (array)($taxonomy->cap); + + //Skip "link_category" because its only cap (manage_links) is already part of a default category. + if ( + ($taxonomy->name === 'link_category') + && ($permissions['manage_terms'] === 'manage_links') + && (count(array_unique($permissions)) === 1) + ) { + continue; + } + + //Skip "nav_menu" and "post_format" because they're intended for internal use and have the same + //caps as the "Category" taxonomy. + if (in_array($taxonomy->name, array('nav_menu', 'post_format')) && $taxonomy->_builtin) { + continue; + } + + $componentId = null; + $isBuiltIn = isset($taxonomy->_builtin) && $taxonomy->_builtin; + if ($isBuiltIn) { + $componentId = self::CORE_COMPONENT_ID; + } else if (isset($this->taxonomyRegistrants[$taxonomy->name])) { + $componentId = $this->taxonomyRegistrants[$taxonomy->name]; + } + + $label = $taxonomy->name; + if (isset($taxonomy->labels, $taxonomy->labels->name) && !empty($taxonomy->labels->name)) { + $label = $taxonomy->labels->name; + } + + $uniqueLabel = $label; + if (isset($usedLabels[$uniqueLabel]) && !$isBuiltIn) { + $uniqueLabel = str_replace('_', ' ', $taxonomy->name); + } + //We want the label in lowercase unless it's an acronym. + if (!preg_match('@([A-Z]{2}|[a-z][A-Z])@', $uniqueLabel)) { + $uniqueLabel = strtolower($uniqueLabel); + } + $usedLabels[$uniqueLabel] = true; + + $registeredTaxonomies[$taxonomy->name] = array( + 'name' => $taxonomy->name, + 'label' => $label, + 'pluralLabel' => $uniqueLabel, + 'componentId' => $componentId, + 'permissions' => $permissions, + ); + + } + + return $registeredTaxonomies; + } + + protected function analysePostTypes($postTypes) { + //Record which components use which CPT capabilities. + foreach ($postTypes as $name => $postType) { + if (empty($postType['componentId']) || !isset($this->knownComponents[$postType['componentId']])) { + continue; + } + $this->knownComponents[$postType['componentId']]->registeredPostTypes[$name] = true; + foreach ($postType['permissions'] as $action => $capability) { + if (isset($this->capabilities[$capability])) { + $this->capabilities[$capability]->addUsage($postType['componentId']); + } + } + + //Add a CPT category to the component that created this post type. + if (empty($postType['isDefault']) && ($postType['componentId'] !== self::CORE_COMPONENT_ID)) { + $componentRoot = $this->getComponentCategory($postType['componentId']); + if ($componentRoot) { + $category = new ameRexPostTypeCategory( + $postType['label'], + $postType['componentId'], + $name, + $postType['permissions'] + ); + $componentRoot->subcategories[] = $category; + } + } + } + } + + protected function analyseTaxonomies($taxonomies) { + //Record taxonomy components and create taxonomy categories for those components. + foreach ($taxonomies as $name => $taxonomy) { + if (empty($taxonomy['componentId'])) { + continue; + } + foreach ($taxonomy['permissions'] as $action => $capability) { + if (isset($this->capabilities[$capability])) { + $this->capabilities[$capability]->addUsage($taxonomy['componentId']); + } + } + + //Add a taxonomy category to the component that created this taxonomy. + if ($taxonomy['componentId'] !== self::CORE_COMPONENT_ID) { + $componentRoot = $this->getComponentCategory($taxonomy['componentId']); + if ($componentRoot) { + $category = new ameRexTaxonomyCategory( + $taxonomy['label'], + $taxonomy['componentId'], + $name, + $taxonomy['permissions'] + ); + $componentRoot->subcategories[] = $category; + } + } + } + } + + /** + * Assign each capability that has known component relationships to one specific component. + * Copy component-related context information to the capability. + */ + protected function assignCapabilitiesToComponents() { + //Figure out which component each capability belongs to. + foreach ($this->capabilities as $capability => $unusedValue) { + $details = $this->capabilities[$capability]; + + if (empty($details->componentId) && !empty($details->usedByComponents)) { + //Sort related components by priority. + if (count($details->usedByComponents) > 1) { + uksort($details->usedByComponents, array($this, 'compareComponents')); + } + //Pick the first component. + $details->componentId = key($details->usedByComponents); + } + + if (!empty($details->componentId)) { + //Copy context information that's relevant to the selected component. + if (isset($details->componentContext[$details->componentId])) { + $propertiesToCopy = array('permissions', 'documentationUrl', 'notes'); + foreach ($propertiesToCopy as $property) { + if (isset($details->componentContext[$details->componentId][$property])) { + $details->$property = $details->componentContext[$details->componentId][$property]; + } + } + } + + if ($details->componentId !== self::CORE_COMPONENT_ID) { + //Add the capability to the component category unless it's already there. + $category = $this->getComponentCategory($details->componentId); + if ($category !== null) { + if (!$category->hasCapability($capability)) { + $category->capabilities[$capability] = true; + } + unset($this->uncategorizedCapabilities[$capability]); + } else { + //This should never happen. If the capability has a component ID, that component + //should already be registered. + trigger_error(sprintf( + '[AME] Capability "%s" belongs to component "%s" but that component appears to be unknown.', + $capability, + $details->componentId + ), E_USER_WARNING); + continue; + } + } + } + } + } + + /** + * @return ameRexCategory + */ + private function loadCoreCategories() { + $root = new ameRexCategory('Core', self::CORE_COMPONENT_ID); + $root->slug = 'default/core'; + + $lines = file_get_contents(__DIR__ . '/data/core-categories.txt'); + $lines = explode("\n", $lines); + + $currentCategory = new ameRexCategory('Placeholder', self::CORE_COMPONENT_ID); + + //Each category starts with a title. The title is followed by one or more indented lines listing + //capability names, one capability per line. Blank lines are ignored. + $lineNumber = 0; + foreach ($lines as $line) { + $lineNumber++; + + //Skip blank lines. + $line = rtrim($line); + if ($line === '') { + continue; + } + + $firstChar = substr($line, 0, 1); + if ($firstChar === ' ' || $firstChar === "\t") { + //Found a capability. + $capability = trim($line); + //Skip unassigned caps. Even core capabilities sometimes get removed as WP development continues. + if (isset($this->capabilities[$capability])) { + $currentCategory->capabilities[$capability] = true; + } + } else { + //Found a "Category title [optional slug]" + if (preg_match('@^(?P%s
[^\[]+)(?:\s+\[(?P [^]]+)])?\s*$@', $line, $matches)) { + //Save the previous category if it matched any capabilities. + if (count($currentCategory->capabilities) > 0) { + $root->addSubcategory($currentCategory); + } + + $title = trim($matches['title']); + $slug = !empty($matches['slug']) ? trim($matches['slug']) : ('default/' . $title); + + $currentCategory = new ameRexCategory($title, self::CORE_COMPONENT_ID); + $currentCategory->slug = $slug; + } + } + } + + //Save the last category. + if (count($currentCategory->capabilities) > 0) { + $root->addSubcategory($currentCategory); + } + + return $root; + } + + protected function analyseAdminMenuCapabilities() { + $menu = $this->menuEditor->get_active_admin_menu(); + if (!empty($menu['tree'])) { + foreach ($menu['tree'] as $item) { + $this->analyseMenuItem($item); + } + } + } + + protected function analyseMenuItem($item, $parent = null) { + $capability = ameUtils::get($item, array('defaults', 'access_level')); + + if (empty($item['custom']) && !empty($item['defaults']) && !empty($capability) && empty($item['separator'])) { + $defaults = $item['defaults']; + $hook = get_plugin_page_hook(ameUtils::get($defaults, 'file', ''), ameUtils::get($defaults, 'parent', '')); + + $rawTitle = ameMenuItem::get($item, 'menu_title', '[Untitled]'); + $fullTitle = trim(strip_tags(ameMenuItem::remove_update_count($rawTitle))); + if ($parent) { + $parentTitle = ameMenuItem::remove_update_count(ameMenuItem::get($parent, 'menu_title', '[Untitled]')); + $fullTitle = trim(strip_tags($parentTitle)) . ' → ' . $fullTitle; + } + + $relatedComponents = array(); + if (!empty($hook)) { + $reflections = $this->getHookReflections($hook); + foreach ($reflections as $reflection) { + $path = $reflection->getFileName(); + $componentId = $this->getComponentIdFromPath($path); + if ($componentId) { + $relatedComponents[$this->getComponentIdFromPath($path)] = true; + } + } + } + + if (isset($this->capabilities[$capability])) { + $this->capabilities[$capability]->menuItems[] = $fullTitle; + $this->capabilities[$capability]->addManyUsages($relatedComponents); + } + } + + if (!empty($item['items'])) { + foreach ($item['items'] as $submenu) { + $this->analyseMenuItem($submenu, $item); + } + } + } + + /** + * @param string $tag + * @return AmeReflectionCallable[] + */ + protected function getHookReflections($tag) { + global $wp_filter; + if (!isset($wp_filter[$tag])) { + return array(); + } + + $reflections = array(); + foreach ($wp_filter[$tag] as $priority => $handlers) { + foreach ($handlers as $index => $callback) { + try { + $reflection = new AmeReflectionCallable($callback['function']); + $reflections[] = $reflection; + } catch (ReflectionException $e) { + //Invalid callback, let's just ignore it. + continue; + } + } + } + return $reflections; + } + + protected function getComponentIdFromPath($absolutePath) { + static $pluginDirectory = null, $muPluginDirectory = null, $themeDirectory = null; + if ($pluginDirectory === null) { + $pluginDirectory = wp_normalize_path(WP_PLUGIN_DIR); + $muPluginDirectory = wp_normalize_path(WPMU_PLUGIN_DIR); + $themeDirectory = wp_normalize_path(WP_CONTENT_DIR . '/themes'); + } + + $absolutePath = wp_normalize_path($absolutePath); + $pos = null; + $type = ''; + if (strpos($absolutePath, $pluginDirectory) === 0) { + $type = 'plugin'; + $pos = strlen($pluginDirectory); + } else if (strpos($absolutePath, $muPluginDirectory) === 0) { + $type = 'mu-plugin'; + $pos = strlen($muPluginDirectory); + } else if (strpos($absolutePath, $themeDirectory) === 0) { + $type = 'theme'; + $pos = strlen($themeDirectory); + } + + if ($pos !== null) { + $nextSlash = strpos($absolutePath, '/', $pos + 1); + if ($nextSlash !== false) { + $componentDirectory = substr($absolutePath, $pos + 1, $nextSlash - $pos - 1); + } else { + $componentDirectory = substr($absolutePath, $pos + 1); + } + return $type . ':' . $componentDirectory; + } + return null; + } + + protected function getRoleData() { + $wpRoles = ameRoleUtils::get_roles(); + $roles = array(); + + $usersByRole = count_users(); + + foreach ($wpRoles->role_objects as $roleId => $role) { + $capabilities = array(); + if (!empty($role->capabilities) && is_array($role->capabilities)) { + $capabilities = $this->menuEditor->castValuesToBool($role->capabilities); + } + + $hasUsers = false; + if (isset($usersByRole['avail_roles'], $usersByRole['avail_roles'][$roleId])) { + $hasUsers = ($usersByRole['avail_roles'][$roleId] > 0); + } + + //Our JS expects the capability map to be an object. It must be an object + //even if it's empty or if all of the keys (i.e. capability names) are numeric. + $capabilities = (object)$capabilities; + + $roles[] = array( + 'name' => $roleId, + 'displayName' => ameUtils::get($wpRoles->role_names, $roleId, $roleId), + 'capabilities' => $capabilities, + 'hasUsers' => $hasUsers, + ); + } + return $roles; + } + + /** + * Get a list of all known capabilities that apply to the current WordPress install. + * + * @param WP_User[] $users List of zero or more users. + * @return array Associative array indexed by capability name. + */ + protected function getAllCapabilities($users = array()) { + //Always include capabilities that are built into WordPress. + $capabilities = $this->getDefaultCapabilities(); + + //Add capabilities assigned to roles. + $capabilities = $capabilities + ameRoleUtils::get_all_capabilities(is_multisite()); + + //Add capabilities of users. + $roleNames = ameRoleUtils::get_role_names(); + foreach ($users as $user) { + $userCaps = $user->caps; + //Remove roles from the capability list. + $userCaps = array_diff_key($userCaps, $roleNames); + $capabilities = $capabilities + $userCaps; + } + + //Add custom capabilities that were created by the user. These persist until manually deleted. + $capabilities = $capabilities + $this->getUserDefinedCaps(); + + $capabilities = $this->menuEditor->castValuesToBool($capabilities); + + uksort($capabilities, 'strnatcasecmp'); + return $capabilities; + } + + protected function getDefaultCapabilities() { + static $defaults = null; + if ($defaults !== null) { + return $defaults; + } + + $defaults = self::loadCapabilities('default-capabilities.txt'); + + if (is_multisite()) { + $defaults = array_merge($defaults, $this->getMultisiteOnlyCapabilities()); + } + + return $defaults; + } + + protected function getMultisiteOnlyCapabilities() { + static $cache = null; + if ($cache === null) { + $cache = self::loadCapabilities('default-multisite-capabilities.txt'); + } + return $cache; + } + + /** + * Load a list of capabilities from a text file. + * + * @param string $fileName + * @param bool|int|string $fillValue Optional. Fill the result array with this value. Defaults to false. + * @return array Associative array with capability names as keys and $fillValue as values. + */ + public static function loadCapabilities($fileName, $fillValue = false) { + $fileName = __DIR__ . '/data/' . $fileName; + if (!is_file($fileName) || !is_readable($fileName)) { + return array(); + } + + $contents = file_get_contents($fileName); + + $capabilities = preg_split('@[\r\n]+@', $contents); + $capabilities = array_map('trim', $capabilities); + $capabilities = array_filter($capabilities, array(__CLASS__, 'isNotEmptyString')); + $capabilities = array_filter($capabilities, array(__CLASS__, 'isNotLineComment')); + + $capabilities = array_fill_keys($capabilities, $fillValue); + + return $capabilities; + } + + protected static function isNotEmptyString($input) { + return $input !== ''; + } + + protected static function isLineComment($input) { + $input = trim($input); + if ($input === '') { + return false; + } + + $firstChar = substr($input, 0, 1); + if ($firstChar === '#' || $firstChar === ';') { + return true; + } + + if (substr($input, 0, 2) === '//') { + return true; + } + + return false; + } + + protected static function isNotLineComment($input) { + return !self::isLineComment($input); + } + + /** + * Get components, capability metadata and possible categories from the capability database. + */ + private function queryCapabilityDatabase() { + $engine = new ameRexCapabilityInfoSearch(); + + $engine->addDataSource(new ameRexSqliteDataSource(__DIR__ . '/data/capability-excerpt.sqlite3')); + //$engine->addDataSource(new ameRexJsonCapabilityDataSource(__DIR__ . '/data/capability-metadata.json')); + + $results = $engine->query(array_keys($this->capabilities), $this->knownComponents); + foreach($results as $capability => $components) { + $this->capabilities[$capability]->addManyUsages($components); + } + } + + private function compareComponents($idA, $idB) { + $a = $this->knownComponents->components[$idA]; + $b = $this->knownComponents->components[$idB]; + + if ($a->isActive && !$b->isActive) { + return -1; + } + if ($b->isActive && !$a->isActive) { + return 1; + } + if ($a->isInstalled && !$b->isInstalled) { + return -1; + } + if ($b->isInstalled && !$a->isInstalled) { + return 1; + } + + return ($b->activeInstalls - $a->activeInstalls); + } + + /** + * @param string $id + * @param WP_Post_Type $postType + */ + public function recordPostTypeOrigin($id, $postType) { + if (!is_admin() || empty($postType) || empty($id)) { + return; + } + + if (isset($postType->_builtin) && $postType->_builtin) { + return; + } + + //Find the last entry that is part of a plugin or theme. + $component = $this->detectCallerComponent(); + if ($component !== null) { + $this->postTypeRegistrants[$id] = $component; + } + } + + public function recordTaxonomyOrigin( + $id, + /** @noinspection PhpUnusedParameterInspection It's part of the filter signature. We can't remove it. */ + $objectType, + $taxonomy = array() + ) { + if (!is_admin() || empty($taxonomy) || empty($id) || !is_array($taxonomy)) { + return; + } + + if (isset($taxonomy['_builtin']) && $taxonomy['_builtin']) { + return; + } + + $component = $this->detectCallerComponent(); + if ($component !== null) { + $this->taxonomyRegistrants[$id] = $component; + } + } + + /** + * Detect the plugin or theme that triggered the current hook. + * If multiple components are involved, only the earliest one will be returned + * (i.e. the one at the bottom of the call stack). + * + * @return null|string + */ + private function detectCallerComponent() { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + //Drop the first three entries because they just contain this method, its caller, + //and an apply_filters or do_action call. + array_shift($trace); + array_shift($trace); + array_shift($trace); + + //Find the last entry that is part of a plugin or theme. + $component = null; + foreach ($trace as $item) { + if (empty($item['file'])) { + continue; + } + + $possibleComponent = $this->getComponentIdFromPath($item['file']); + if ($possibleComponent) { + $component = $possibleComponent; + } + } + return $component; + } + + private function queryInstalledComponents() { + $installedPlugins = get_plugins(); + foreach ($installedPlugins as $pluginFile => $plugin) { + $pathComponents = explode('/', $pluginFile, 2); + if (count($pathComponents) < 2) { + continue; + } + $component = new ameRexComponent( + 'plugin:' . $pathComponents[0], + ameUtils::get($plugin, 'Name', $pathComponents[0]) + ); + $component->isInstalled = true; + $component->isActive = is_plugin_active($pluginFile); + $this->knownComponents[$component->id] = $component; + } + + $installedMuPlugins = get_mu_plugins(); + foreach($installedMuPlugins as $pluginFile => $plugin) { + $component = new ameRexComponent( + 'mu-plugin:' . $pluginFile, + ameUtils::get($plugin, 'Name', $pluginFile) + ); + $component->isInstalled = true; + $component->isActive = true; //mu-plugins are always active. + $this->knownComponents[$component->id] = $component; + } + + $activeThemeSlugs = array(get_stylesheet(), get_template()); + foreach ($activeThemeSlugs as $slug) { + $componentId = 'theme:' . $slug; + if (isset($this->knownComponents[$componentId])) { + continue; + } + $theme = wp_get_theme($slug); + if (!empty($theme)) { + $component = new ameRexComponent($componentId, $theme->get('Name')); + $component->isActive = true; + $component->isInstalled = true; + $this->knownComponents[$component->id] = $component; + } + } + } + + /** + * @param $componentId + * @return ameRexCategory|null + */ + private function getComponentCategory($componentId) { + if (!isset($this->componentRootCategories[$componentId])) { + if (!isset($this->knownComponents[$componentId])) { + return null; + } + $category = new ameRexCategory($this->knownComponents[$componentId]->name, $componentId); + $category->slug = 'components/' . $componentId; + $this->componentRootCategories[$componentId] = $category; + } + return $this->componentRootCategories[$componentId]; + } + + /** + * Group capabilities that look like they belong to a post type but are not used by any registered post types. + * This could be stuff left behind by an uninstalled plugin, or just a set of similar capabilities. + * + * @return ameRexCategory[] + */ + protected function findProbablePostTypeCategories() { + $potentialPostTypes = array(); + $foundCategories = array(); + + //At the moment, WordPress database schema limits post types to 20 characters. + $namePattern = '(?P .{1,20}?)s?'; + $cptPatterns = array( + '@^edit_(?:(?:others|private|published)_)?' . $namePattern . '$@', + '@^delete_(?:(?:others|private|published)_)?' . $namePattern . '$@', + '@^publish_' . $namePattern . '$@', + + '@^read_private_' . $namePattern . '$@', + '@^read_' . $namePattern . '$@', + + //WooCommerce stuff + '@^(assign|edit|manage|delete)_' . $namePattern . '_terms$@', + ); + + foreach ($this->uncategorizedCapabilities as $capability => $unused) { + foreach ($cptPatterns as $pattern) { + if (preg_match($pattern, $capability, $matches)) { + $postType = $matches['post_type']; + + //Unknown CPT-like capability. + if (!isset($potentialPostTypes[$postType])) { + $potentialPostTypes[$postType] = array(); + } + $potentialPostTypes[$postType][$capability] = $capability; + + break; + } + } + } + + //Empirically, real post types have at least 3 associated capabilities. + foreach ($potentialPostTypes as $postType => $typeCaps) { + if (count($typeCaps) >= 3) { + //Note that this group does not correspond to an existing post type. It's just a set of similar caps. + $title = ameUtils::ucWords(str_replace('_', ' ', $postType)); + if (substr($title, -1) !== 's') { + $title .= 's'; //Post type titles are usually plural. + } + + $category = new ameRexCategory($title); + $category->capabilities = array_fill_keys($typeCaps, true); + $foundCategories[] = $category; + + //Now that we know which group these caps belong to, remove them from consideration. + foreach ($typeCaps as $capability) { + unset($this->uncategorizedCapabilities[$capability]); + } + } + } + + return $foundCategories; + } + + private function groupSimilarCapabilities() { + $stopWords = $this->getPrefixStopWords(); + + //Find the common prefix of each component root, if there is one. + foreach ($this->componentRootCategories as $category) { + if (!empty($category->capabilities) && (count($category->capabilities) > 1)) { + $possiblePrefix = key($category->capabilities); + foreach ($category->capabilities as $capability => $unusedValue) { + for ($i = 0; $i < min(strlen($possiblePrefix), strlen($capability)); $i++) { + if ($possiblePrefix[$i] !== $capability[$i]) { + if ($i >= 1) { + $possiblePrefix = substr($possiblePrefix, 0, $i); + } else { + $possiblePrefix = ''; + } + break; + } + } + + if ($possiblePrefix === '') { + break; + } + } + + //The prefix must be at least 2 characters long and must not consist entirely of stopwords. + if (strlen($possiblePrefix) >= 2) { + $tokens = $this->tokenizeCapability($possiblePrefix); + $foundStopWords = 0; + foreach ($tokens as $token) { + if (isset($stopWords[strtolower($token)])) { + $foundStopWords++; + } + } + if ($foundStopWords === count($tokens)) { + continue; + } + + $prefix = implode(' ', array_slice($tokens, 0, 2)); + $this->componentCapPrefixes[$prefix] = $category; + } + } + } + + $possibleCategories = array(); + foreach ($this->uncategorizedCapabilities as $capability => $unusedValue) { + $tokens = $this->tokenizeCapability($capability); + $upperLimit = min(2, count($tokens) - 1); + + $prefix = null; + for ($i = 0; $i < $upperLimit; $i++) { + if ($prefix === null) { + $prefix = $tokens[$i]; + } else { + $prefix .= ' ' . $tokens[$i]; + } + if (isset($stopWords[$tokens[$i]]) || (strlen($tokens[$i]) < 2)) { + continue; + } + + //Check if one of the existing component categories has the same prefix + //and add this capability there. + if (isset($this->componentCapPrefixes[$prefix])) { + $this->componentCapPrefixes[$prefix]->addCapabilityToDefaultLocation($capability); + unset($this->uncategorizedCapabilities[$capability]); + + $componentId = $this->componentCapPrefixes[$prefix]->componentId; + if ($componentId !== null) { + $this->capabilities[$capability]->addUsage($componentId); + if (empty($this->capabilities[$capability]->componentId)) { + $this->capabilities[$capability]->componentId = $componentId; + } + } + } + + if (!isset($possibleCategories[$prefix])) { + $possibleCategories[$prefix] = array(); + } + $possibleCategories[$prefix][$capability] = true; + } + } + + uasort($possibleCategories, array($this, 'compareArraySizes')); + + $approvedCategories = array(); + foreach ($possibleCategories as $prefix => $capabilities) { + $capabilities = array_intersect_key($capabilities, $this->uncategorizedCapabilities); + if (count($capabilities) < 3) { + continue; + } + + $title = $prefix; + //Convert all-lowercase to Title Case, but preserve stuff that already has mixed case. + if (strtolower($title) === $title) { + $title = ameUtils::ucWords($title); + } + + //No vowels = probably an acronym. + if (!preg_match('@[aeuio]@', $title)) { + $title = strtoupper($title); + } + + $category = new ameRexCategory($title); + $category->capabilities = $capabilities; + $approvedCategories[] = $category; + foreach ($capabilities as $capability => $unused) { + unset($this->uncategorizedCapabilities[$capability]); + } + } + + return $approvedCategories; + } + + private function tokenizeCapability($capability) { + return preg_split('@[\s_\-]@', $capability, -1, PREG_SPLIT_NO_EMPTY); + } + + private function getPrefixStopWords() { + static $stopWords = null; + if ($stopWords === null) { + $stopWords = array( + 'edit', + 'delete', + 'add', + 'list', + 'manage', + 'read', + 'others', + 'private', + 'published', + 'publish', + 'terms', + 'view', + 'create', + 'settings', + 'options', + 'option', + 'setting', + 'update', + 'install', + ); + $stopWords = array_fill_keys($stopWords, true); + } + return $stopWords; + } + + private function compareArraySizes($a, $b) { + return count($b) - count($a); + } + + public function ajaxUpdateUserPreferences() { + check_ajax_referer(self::UPDATE_PREFERENCES_ACTION); + + @header('Content-Type: application/json; charset=' . get_option('blog_charset')); + if (!$this->userCanAccessModule()) { + echo json_encode(array('error' => 'Access denied')); + exit; + } + + $post = $this->menuEditor->get_post_params(); + if (!isset($post['preferences']) || !is_string($post['preferences'])) { + echo json_encode(array('error' => 'The "preferences" field is missing or invalid.')); + exit; + } + + $preferences = json_decode($post['preferences'], true); + if ($preferences === null) { + echo json_encode(array('error' => 'The "preferences" field is not valid JSON.')); + exit; + } + + if (!is_array($preferences)) { + echo json_encode(array('error' => 'The "preferences" field is not valid. Expected an associative array.')); + exit; + } + + update_user_meta(get_current_user_id(), self::USER_PREFERENCE_KEY, json_encode($preferences)); + + echo json_encode(array('success' => true)); + exit; + } + + public function handleSettingsForm($post = array()) { + if (!$this->userCanAccessModule()) { + wp_die("You don't have sufficient permissions to change these settings."); + } + + $redirectParams = array(); + if (!empty($post['selectedActor'])) { + $redirectParams['selected_actor'] = strval($post['selectedActor']); + } + + $validator = new ameRexSettingsValidator( + $post['settings'], + $this->getAllCapabilities(array(wp_get_current_user())), + $this->getUserDefinedCaps(), + ameUtils::get($this->loadSettings(), 'editableRoles', array()), + $this->getEffectiveEditableRoles(), + $this->menuEditor + ); + + $errors = $validator->validate(); + $this->storeSettingsErrors($errors); + + $shouldUpdateNetwork = !empty($post['isGlobalUpdate']) && is_multisite() && is_super_admin(); + $totalChanges = $validator->getTotalChangeCount(); + + //var_dump($totalChanges, $post); + //exit; + + if (($totalChanges <= 0) && !$shouldUpdateNetwork) { + $redirectParams['no-changes-made'] = 1; + wp_redirect($this->getTabUrl($redirectParams)); + exit; + } + + if ($shouldUpdateNetwork && ($totalChanges < 1)) { + $totalChanges = 1; //A network update is always at least one change. + } + + if ($this->backupsEnabled) { + //Make a backup before actually changing anything. + $currentUser = wp_get_current_user(); + $this->createRoleBackup(sprintf( + 'Automatic backup before applying %d changes made by %s', + $totalChanges, + $currentUser->user_login + )); + } + + //Save user defined capabilities. + if ($validator->getUserDefinedCaps() !== null) { + $this->saveUserDefinedCaps($validator->getUserDefinedCaps()); + } + + //Apply role changes. + //------------------- + $wpRoles = ameRoleUtils::get_roles(); + + //Delete roles. + foreach ($validator->getRolesToDelete() as $id => $role) { + $wpRoles->remove_role($id); + } + + //Create roles. + foreach ($validator->getRolesToCreate() as $id => $role) { + $wpRoles->add_role($id, $role['displayName'], $role['capabilities']); + } + + //Update role capabilities and display names. + $rolesToModify = $validator->getRolesToModify(); + if (!empty($rolesToModify)) { + foreach ($rolesToModify as $id => $role) { + //Rename the role. + if ($wpRoles->roles[$id]['name'] !== $role['displayName']) { + $wpRoles->roles[$id]['name'] = $role['displayName']; + } + $wpRoles->roles[$id]['capabilities'] = $role['capabilities']; + } + //Save role data. + update_option($wpRoles->role_key, $wpRoles->roles); + } + + //Apply role settings to all network sites if requested. We'll do that even if the settings + //weren't changed, which lets you use this feature to normalize role settings across the network. + if ($shouldUpdateNetwork) { + $result = $this->updateNetworkRoles(get_option($wpRoles->role_key)); + if (is_wp_error($result)) { + $errors[] = $result; + $this->storeSettingsErrors($errors); + } + } + + //Apply user changes. + //------------------- + + foreach ($validator->getUsersToModify() as $userId => $modifiedUser) { + $newCaps = $modifiedUser['capabilities']; + $newRoles = $modifiedUser['roles']; + $user = $validator->getExistingUser($userId); + + //We have to go through the trouble of removing/adding each role individually + //because some plugins use the "add_user_role" and "remove_user_role" hooks. + $oldRoles = (isset($user->roles) && is_array($user->roles)) ? $user->roles : array(); + $removedRoles = array_diff($oldRoles, $newRoles); + $addedRoles = array_diff($newRoles, $oldRoles); + foreach ($removedRoles as $role) { + $user->remove_role($role); + } + foreach ($addedRoles as $role) { + $user->add_role($role); + } + + //Now that the necessary hooks have been triggered, we can just overwrite the caps array. + //This is faster than calling add_cap() a bunch of times and it lets us precisely control + //the order of roles in the array. + $user->remove_all_caps(); + $user->roles = array(); + $user->caps = array_merge($newCaps, array_fill_keys($newRoles, true)); + + update_user_meta($user->ID, $user->cap_key, $user->caps); + $user->get_role_caps(); + $user->update_user_level_from_caps(); + } + + //Save editable roles. + $this->loadSettings(); + $this->settings['editableRoles'] = $validator->getNewEditableRoleSettings(); + $this->saveSettings(); + + $redirectParams['updated'] = 1; + wp_redirect($this->getTabUrl($redirectParams)); + exit; + + //TODO: Consider creating revisions in an update_option filter (flushed on shutdown), not here. Also in add_option. + //TODO: Maybe leave revision log formatting until time of display. It can be built based on change lists, I think. + + //TODO: It's still useful to take a backup/create a revision before updating if there have been no backups in X days. + //The roles could have changed while the plugin is inactive, in which case the previous revision could be out of date. + + //TODO: Save trashed roles. + + /* + * Remove all roles that don't exist in the role list. + For each existing role: + Add missing caps, remove deleted caps. + Keep a list of caps created with AME even if they're unused. + Create roles that don't exist. + Rename roles where the current name doesn't match the new one. + + Update user capabilities. + Update user roles (see validateUserRoleChange) + + (Seems straightforward. Could be done directly if API is too slow.) + Validation: + Only change editable roles. + Some capabilities are only available to super admins. + Cannot create capabilities with "<>&" characters. + Validate roles names and display names. + Don't delete the default role and used roles. + */ + } + + /** + * Update role settings across the entire Multisite network. Applies the same settings to all sites. + * + * @param mixed $roleData + * @return bool|WP_Error + */ + private function updateNetworkRoles($roleData) { + global $wpdb; + /** @var wpdb $wpdb */ + + if (!is_multisite()) { + return new WP_Error( + 'ame_not_multisite', + 'Cannot update roles on all sites because this is not a Multisite network.' + ); + } + + if (empty($roleData)) { + return new WP_Error('ame_invalid_role_data', 'Role data is invalid.'); + } + + if (!function_exists('get_sites')) { + return new WP_Error( + 'ame_wp_incompatible', + 'The plugin does not support this feature in the current WordPress version.' + ); + } + + //When a site uses an external object cache (e.g. Redis), we might need to remove the '$prefix_user_roles' + //option from the cache to force the caching plugin to load the updated roles from the database. + //This could be slow because it involves switching to each site, so it's off by default. + $shouldClearCache = is_callable('wp_cache_switch_to_blog') + && is_callable('wp_using_ext_object_cache') + && wp_using_ext_object_cache() + && apply_filters('admin_menu_editor-clear_role_cache', false); + $originalBlogId = is_callable('get_current_blog_id') ? get_current_blog_id() : null; + + $sites = get_sites(array( + /* + * As of this writing, WP documentation doesn't mention any officially supported way + * to make get_sites() return all available results. There are unofficial workarounds, + * but simply specifying a very high number should be good enough for most situations. + * We'll probably hit the PHP execution time limit before we hit the result limit. + */ + 'number' => 1000000, + 'fields' => 'ids', + )); + $serializedData = serialize($roleData); + + foreach ($sites as $siteId) { + $prefix = $wpdb->get_blog_prefix($siteId); + $tableName = $prefix . 'options'; + $optionName = $prefix . 'user_roles'; + + $query = $wpdb->prepare( + "UPDATE {$tableName} SET option_value = %s WHERE option_name = %s LIMIT 1", + array($serializedData, $optionName) + ); + + $result = $wpdb->query($query); + if ($result === false) { + $errorMessage = sprintf('Failed to update site with ID %d.', $siteId); + if (!empty($wpdb->last_error)) { + $errorMessage .= ' Database error: ' . $wpdb->last_error; + } + return new WP_Error('ame_db_error', $errorMessage); + } + + //Clear the option cache on the target site. + if ($shouldClearCache) { + wp_cache_switch_to_blog($siteId); + wp_cache_delete($optionName, 'options'); + } + } + + if ($shouldClearCache && ($originalBlogId !== null)) { + wp_cache_switch_to_blog($originalBlogId); + } + + return true; + } + + /** @noinspection PhpUnusedPrivateMethodInspection */ + private function tempGenerateChangeSummary($role = null) { + global $wpRoles; + $id = ''; + + if (!empty($rolesToDelete)) { + $deletedIds = array_keys($rolesToDelete); + if (count($deletedIds) === 1) { + $summary[] = 'Deleted ' . $deletedIds[0]; + } else if (count($deletedIds) === 2) { + $summary[] = 'Deleted ' . implode(' and ', $deletedIds); + } else { + $summary[] = 'Deleted ' . count($deletedIds) . ' roles'; + } + } + + if (!empty($rolesToCreate)) { + $createdIds = array_keys($rolesToCreate); + $summary[] = 'Created ' . $this->formatPhraseList($createdIds, '%d roles'); + } + + $renamedRoles[] = sprintf('"%1$s" to %2$s', $wpRoles->roles[$id]['name'], $role['displayName']); + + $oldGrantedCaps = array_filter($wpRoles->roles[$id]['capabilities']); + $newGrantedCaps = array_filter($role['capabilities']); + $addedCaps = array_diff_key($newGrantedCaps, $oldGrantedCaps); + $removedCaps = array_diff_key($oldGrantedCaps, $newGrantedCaps); + + $changes = array(); + if (!empty($addedCaps)) { + $changes[] = count($addedCaps) . ' added'; + } + if (!empty($removedCaps)) { + $changes[] = count($removedCaps) . ' removed'; + } + $capSummaries[] = $id . ' (' . implode(', ', $changes) . ')'; + + //Add renames and cap changes to the summary. + if (!empty($renamedRoles)) { + $summary[] = 'Renamed ' . $this->formatPhraseList($renamedRoles, '%d roles', 1); + } + if (!empty($capSummaries)) { + $summary[] = 'Changed capabilities: ' . implode(', ', $capSummaries); + } + } + + private function formatPhraseList($items, $combinedTemplate = '%d items', $limit = 2) { + $itemCount = count($items); + if ($itemCount === 1) { + return $items[0]; + } else if ($itemCount === 2) { + return implode(' and ', $items); + } else if ($itemCount <= $limit) { + return implode(', ', $items); + } + return sprintf($combinedTemplate, $itemCount); + } + + /** + * @param array $errors + */ + private function storeSettingsErrors($errors) { + if (empty($errors)) { + delete_transient(self::SETTINGS_ERROR_TRANSIENT); + return; + } + set_transient(self::SETTINGS_ERROR_TRANSIENT, $errors, 30 * 60); + $this->cachedSettingsErrors = $errors; + } + + /** + * @return array + */ + private function fetchSettingsErrors() { + $storedErrors = get_transient(self::SETTINGS_ERROR_TRANSIENT); + if ($storedErrors !== false) { + delete_transient(self::SETTINGS_ERROR_TRANSIENT); + $this->cachedSettingsErrors = (array)$storedErrors; + } + return $this->cachedSettingsErrors; + } + + protected function getTemplateVariables($templateName) { + $variables = parent::getTemplateVariables($templateName); + $variables['settingsErrors'] = $this->fetchSettingsErrors(); + return $variables; + } + + /** + * @param string $comment + * @param string|null $roleData + * @return bool + */ + private function createRoleBackup($comment = '', $roleData = null) { + //TODO: If we're going to do backups, remember to create the table if it doesn't exist. + //TODO: Delete the table when the plugin is uninstalled. + if ($roleData === null) { + $wpRoles = ameRoleUtils::get_roles(); + $roleData = get_option($wpRoles->role_key); + } + + /** @var wpdb */ + global $wpdb; + + $result = $wpdb->insert( + $this->backupTable, + array( + 'created_on' => gmdate('Y-m-d H:i:s'), + 'site_id' => get_current_blog_id(), + 'user_id' => get_current_user_id(), + 'role_data' => serialize($roleData), + 'comment' => $comment, + ), + array('%s', '%d', '%d', '%s', '%s') + ); + return ($result !== false); + } + + /** + * Delete old role data backups. + * + * We keep either the last 10 backups or the last 30 days of backups, whichever is greater. + */ + public function deleteOldRoleBackups() { + /** @var wpdb */ + global $wpdb; + + $nthItemDate = $wpdb->get_var( + 'SELECT created_on FROM ' . $this->backupTable + . ' WHERE 1 ORDER BY created_on DESC LIMIT 1 OFFSET 10' + ); + + if (empty($nthItemDate)) { + return; //There are 10 or fewer backups. Do nothing. + } + + $survivalThreshold = gmdate( + 'Y-m-d H:i:s', + min(strtotime($nthItemDate . ' UTC'), strtotime('-30 days')) + ); + + $wpdb->query($wpdb->prepare( + 'DELETE FROM ' . $this->backupTable . + ' WHERE created_on <= %s', $survivalThreshold + )); + } + + /** + * Check if a user can access the role editor module. + * + * @param int|null $userId Optional user ID. Defaults to the current user. + * @return bool + */ + private function userCanAccessModule($userId = null) { + if (($userId === null) || ($userId === get_current_user_id())) { + return $this->menuEditor->current_user_can_edit_menu() && current_user_can(self::REQUIRED_CAPABILITY); + } + $user = get_user_by('id', $userId); + if (!$user) { + return false; + } + return $this->menuEditor->user_can_edit_menu($userId) + && $user->has_cap(self::REQUIRED_CAPABILITY); + } + + private function saveUserDefinedCaps($capabilities) { + delete_site_option(self::USER_DEFINED_CAP_KEY); + + if (empty($capabilities)) { + return; + } + update_site_option(self::USER_DEFINED_CAP_KEY, $capabilities); + } + + /** + * Get a list of capabilities that were created by the user(s). + * + * @return array [capabilityName => arbitraryValue] + */ + private function getUserDefinedCaps() { + $caps = get_site_option(self::USER_DEFINED_CAP_KEY, array()); + if (!is_array($caps)) { + return array(); + } + return $caps; + } + + /** + * Apply "editable roles" settings. + * + * @param array $editableRoles + * @return array + */ + public function filterEditableRoles($editableRoles) { + //Sanity check: The role list should be an array or something array-like. + if ( !is_array($editableRoles) && !($editableRoles instanceof Traversable) ) { + return $editableRoles; + } + + //Do nothing if the core user API hasn't been loaded yet. There is at least one plugin that tries to get + //editable roles before WordPress loads the user API and determines which user is logged in. + if ( !is_callable('wp_get_current_user') ) { + return $editableRoles; + } + + // Do nothing if the overall strategy is "none" ("leave unchanged"). + $user = wp_get_current_user(); + if ( + isset($this->cachedOverallEditableRoleStrategy[$user->ID]) + && ($this->cachedOverallEditableRoleStrategy[$user->ID] === 'none') + ) { + return $editableRoles; + } + + //It's possible that another plugin has already removed some roles from the array. We'll need the full list + //so that we can restore enabled roles if the user has selected the "user-defined-list" strategy. + if (function_exists('wp_roles')) { + $allRoles = array_merge(wp_roles()->roles, $editableRoles); + } else { + $allRoles = $editableRoles; + } + + //Try the cache first. + if ( isset($this->cachedEditableRoles[$user->ID]) ) { + return $this->cachedEditableRoles[$user->ID]->filter($allRoles, $editableRoles); + } + + //A super admin always has full access to everything. Do not remove any roles. + if (is_multisite() && is_super_admin()) { + return $editableRoles; + } + + $settings = ameUtils::get($this->loadSettings(), 'editableRoles', array()); + $userRoles = $this->menuEditor->get_user_roles($user); + + //User-specific settings have precedence. + //For users, "auto" means "use role settings". + $userActorId = 'user:' . $user->user_login; + if ( ameUtils::get($settings, array($userActorId, 'strategy'), 'auto') !== 'auto' ) { + $userSettings = $settings[$userActorId]; + if ($userSettings['strategy'] === 'none') { + //Leave the roles unchanged. + $this->cachedOverallEditableRoleStrategy[$user->ID] = 'none'; + return $editableRoles; + } else if ($userSettings['strategy'] === 'user-defined-list') { + //Allow editing only those roles that are on the list. + $filteredResult = array(); + $allowedRoles = ameUtils::get($userSettings, 'userDefinedList', array()); + foreach($allRoles as $roleId => $role) { + if ( isset($allowedRoles[$roleId]) ) { + $filteredResult[$roleId] = $role; + } + } + $this->cachedEditableRoles[$user->ID] = new ameEditableRoleReplacer( + array_fill_keys(array_keys($filteredResult), true) + ); + $this->cachedOverallEditableRoleStrategy[$user->ID] = 'user-defined-list'; + return $filteredResult; + } + //We'll only reach this line if the user's strategy setting is not valid. + //In that case, let's leave the role list unchanged. + return $editableRoles; + } + + $leaveUnchanged = true; + $hasAnyUserDefinedList = false; + $autoDisabledRoles = array(); + $filteredResult = array(); + + foreach($allRoles as $roleId => $role) { + $wasEnabled = isset($editableRoles[$roleId]); + $canAutoDisable = false; + + //Include this role if at least one of the user's roles is allowed to edit it. + foreach($userRoles as $userRoleId) { + $actorId = 'role:' . $userRoleId; + + $strategy = ameUtils::get($settings, array($actorId, 'strategy'), 'auto'); + $leaveUnchanged = $leaveUnchanged && ($strategy === 'none'); + + if ($strategy === 'user-defined-list') { + $hasAnyUserDefinedList = true; + if ( isset($settings[$actorId]['userDefinedList'][$roleId]) ) { + $filteredResult[$roleId] = $role; + break; + } + } else if ( ($strategy === 'auto') ) { + //Shortcut: A user with role X can assign role X to other users (assuming that they can edit users). + if ( $roleId === $userRoleId ) { + $shouldLeaveEnabled = true; + } else { + //Does the target role have the same or fewer capabilities as the user's role? + $targetCaps = $this->getEnabledCoreCapabilitiesForRole($roleId, $role); + $sameCaps = array_intersect_key( + $targetCaps, + $this->getEnabledCoreCapabilitiesForRole($userRoleId) + ); + $shouldLeaveEnabled = (count($sameCaps) === count($targetCaps)); + } + + $canAutoDisable = !$shouldLeaveEnabled; + if ( $wasEnabled && $shouldLeaveEnabled ) { + $filteredResult[$roleId] = $role; + break; + } + } else if ($strategy === 'none') { + if ($wasEnabled) { + $filteredResult[$roleId] = $role; + } + } + } + + if ($canAutoDisable && !isset($filteredRoles[$roleId])) { + $autoDisabledRoles[] = $roleId; + } + } + + //Are all of the roles set to "none" = leave unchanged? + if ($leaveUnchanged) { + $this->cachedOverallEditableRoleStrategy[$user->ID] = 'none'; + return $editableRoles; + } + + $overallStrategy = $hasAnyUserDefinedList ? 'user-defined-list' : 'auto'; + $this->cachedOverallEditableRoleStrategy[$user->ID] = $overallStrategy; + + //We won't need the capability cache again unless something changes or replaces the current user mid-request. + //That's probably going to be rare, so we can throw away the cache to free up some RAM. + $this->cachedEnabledRoleCaps = array(); + //Update the user cache. + if ($overallStrategy === 'auto') { + $this->cachedEditableRoles[$user->ID] = new ameEditableRoleLimiter($autoDisabledRoles); + } else { + $this->cachedEditableRoles[$user->ID] = new ameEditableRoleReplacer( + array_fill_keys(array_keys($filteredResult), true) + ); + } + + if (!$this->isRoleCacheClearingHookSet) { + $this->isRoleCacheClearingHookSet = true; + //Clear cache when user roles or capabilities change. + add_action('updated_user_meta', array($this, 'clearEditableRoleCache'), 10, 0); + add_action('deleted_user_meta', array($this, 'clearEditableRoleCache'), 10, 0); + //Clear cache when switching to another site because users can have different roles + //on different sites. + add_action('switch_blog', array($this, 'clearEditableRoleCache'), 10, 0); + } + + return $filteredResult; + } + + /** + * @param string $roleId + * @param array|null $roleData + * @return boolean[] + */ + private function getEnabledCoreCapabilitiesForRole($roleId, $roleData = null) { + if (isset($this->cachedEnabledRoleCaps[$roleId])) { + return $this->cachedEnabledRoleCaps[$roleId]; + } + + if ($roleData) { + $capabilities = isset($roleData['capabilities']) ? $roleData['capabilities'] : null; + } else { + $roleObject = get_role($roleId); + $capabilities = isset($roleObject->capabilities) ? $roleObject->capabilities : null; + } + if (!isset($capabilities)) { + return array(); + } + + $enabledCaps = array_filter($capabilities); + + //Keep only core capabilities like "edit_posts" and filter out custom capabilities added by plugins or themes. + $enabledCaps = array_intersect_key($enabledCaps, $this->getDefaultCapabilities()); + + $this->cachedEnabledRoleCaps[$roleId] = $enabledCaps; + return $enabledCaps; + } + + /** + * Get roles that the current user can edit in the role editor. Unlike get_editable_roles(), this method should + * include any special roles that do not show up in the role list when editing a user, like the forum roles + * created by bbPress. + * + * @return array + */ + private function getEffectiveEditableRoles() { + $editableRoles = get_editable_roles(); + if (empty($editableRoles) || !is_array($editableRoles)) { + $editableRoles = array(); + } + if (function_exists('bbp_get_dynamic_roles')) { + $userId = get_current_user_id(); + if ( + !isset($this->cachedOverallEditableRoleStrategy[$userId]) + || ($this->cachedOverallEditableRoleStrategy[$userId] !== 'user-defined-list') + ) { + $bbPressRoles = bbp_get_dynamic_roles(); + $editableRoles = array_merge($bbPressRoles, $editableRoles); + } + } + return $editableRoles; + } + + public function clearEditableRoleCache() { + $this->cachedEditableRoles = array(); + $this->cachedOverallEditableRoleStrategy = array(); + $this->cachedEnabledRoleCaps = array(); + } + + /** + * Prevent less-privileged users from editing more privileged users. + * + * @param array $requiredCaps List of primitive capabilities (output). + * @param string $capability The meta capability (input). + * @param int $thisUserId The user that's trying to do something. + * @param array $args + * @return array + */ + public function restrictUserEditing($requiredCaps, $capability, $thisUserId, $args) { + static $editUserCaps = array('edit_user', 'delete_user', 'promote_user'); + if (!in_array($capability, $editUserCaps) || !isset($args[0])) { + return $requiredCaps; + } + + /** @var int $targetUserId The user that might be edited or deleted. */ + $targetUserId = intval($args[0]); + + $thisUserId = intval($thisUserId); + $isMultisite = is_multisite(); + $isSuperAdmin = $isMultisite && is_super_admin($thisUserId); + + //Super Admins can edit everything. + if ($isSuperAdmin) { + return $requiredCaps; + } + + $accessDenied = array_merge($requiredCaps, array('do_not_allow')); + + //Only Super Admins can edit other Super Admins. + if ($isMultisite && is_super_admin($targetUserId) && !$isSuperAdmin) { + return $accessDenied; + } + + //Users that don't have access to the role editor can't edit users that do have access. + if (!$this->userCanAccessModule($thisUserId) && $this->userCanAccessModule($targetUserId)) { + return $accessDenied; + } + + //Finally, a user can only edit those other users that have an editable role. + //This part only works with the current user because get_editable_roles() does not take a user parameter. + if (($thisUserId === get_current_user_id()) && ($thisUserId !== $targetUserId) ) { + //The file that defines get_editable_roles() is only loaded in the admin back-end even though + //the "edit_user" capability is also used in the front-end, e.g. when adding an "Edit" link + //to the Toolbar/Admin Bar on author pages. + if (!function_exists('get_editable_roles')) { + return $requiredCaps; + } + $editableRoles = get_editable_roles(); + + $strategy = 'auto'; + if ( isset($this->cachedOverallEditableRoleStrategy[$thisUserId]) ) { + $strategy = $this->cachedOverallEditableRoleStrategy[$thisUserId]; + } + + //Don't apply any further restrictions if all editable role settings are set to "leave unchanged" + //for this user. + if ($strategy === 'none') { + return $requiredCaps; + } + + if (function_exists('bbp_get_dynamic_roles')) { + $bbPressRoles = bbp_get_dynamic_roles(); + } else { + $bbPressRoles = array(); + } + + $targetUser = get_user_by('id', $targetUserId); + $roles = (isset($targetUser->roles) && is_array($targetUser->roles)) ? $targetUser->roles : array(); + foreach($roles as $roleId) { + /* + * = bbPress compatibility fix = + * + * bbPress always removes its special roles (like "Participant") from the editable role list. As far + * as I can tell, the intent is to prevent people from bypassing bbPress settings and manually giving + * those roles to users. + * + * This should not automatically prevent administrators from editing users who have any bbPress roles. + * Therefore, let's ignore bbPress roles here unless the user has custom editable role settings. + */ + if (array_key_exists($roleId, $bbPressRoles) && ($strategy !== 'user-defined-list')) { + continue; + } + + if (!array_key_exists($roleId, $editableRoles)) { + return $accessDenied; + } + } + } + + return $requiredCaps; + } + + public function getExportOptionLabel() { + return '"Editable Roles" settings'; + } +} + +interface ameEditableRoleFilter { + /** + * @param array $allRoles + * @param array $editableRoles + * @return array Filtered editable roles. + */ + public function filter($allRoles, $editableRoles); +} + +/** + * Replaces the list of editable roles with the specified list. + * Any changes that were made by other plugins will be overwritten. + */ +class ameEditableRoleReplacer implements ameEditableRoleFilter { + private $enabledRoles; + + /** + * @param array $enabledRoles + */ + public function __construct($enabledRoles) { + $this->enabledRoles = $enabledRoles; + } + + public function filter($allRoles, $editableRoles) { + $result = array(); + foreach ($allRoles as $roleId => $role) { + if ( isset($this->enabledRoles[$roleId]) ) { + $result[$roleId] = $role; + } + } + return $result; + } + +} + +/** + * Removes the specified roles from the list of editable roles. + */ +class ameEditableRoleLimiter implements ameEditableRoleFilter { + private $rolesToRemove; + + /** + * @param string[] $rolesToRemove + */ + public function __construct($rolesToRemove) { + $this->rolesToRemove = $rolesToRemove; + } + + public function filter($allRoles, $editableRoles) { + foreach ($this->rolesToRemove as $roleId) { + if ( array_key_exists($roleId, $editableRoles) ) { + unset($editableRoles[$roleId]); + } + } + return $editableRoles; + } +} \ No newline at end of file diff --git a/extras/modules/role-editor/data/capability-excerpt.sqlite3 b/extras/modules/role-editor/data/capability-excerpt.sqlite3 new file mode 100644 index 0000000..914c113 Binary files /dev/null and b/extras/modules/role-editor/data/capability-excerpt.sqlite3 differ diff --git a/extras/modules/role-editor/data/core-categories.txt b/extras/modules/role-editor/data/core-categories.txt new file mode 100644 index 0000000..7ef3256 --- /dev/null +++ b/extras/modules/role-editor/data/core-categories.txt @@ -0,0 +1,63 @@ +Administration + edit_dashboard + export + import + manage_options + moderate_comments + unfiltered_html + update_core + +Plugins + activate_plugins + delete_plugins + edit_plugins + install_plugins + update_plugins + +Themes + delete_themes + edit_themes + edit_theme_options + install_themes + update_themes + switch_themes + +Users + add_users + create_users + delete_users + edit_users + list_users + promote_users + remove_users + +Multisite [default/multisite] + create_sites + delete_sites + manage_sites + manage_network + manage_network_users + manage_network_themes + manage_network_plugins + manage_network_options + upgrade_network + +Other [default/other] + read + upload_files + manage_links + unfiltered_upload + +Deprecated [default/deprecated] + edit_files + level_0 + level_1 + level_2 + level_3 + level_4 + level_5 + level_6 + level_7 + level_8 + level_9 + level_10 \ No newline at end of file diff --git a/extras/modules/role-editor/data/default-capabilities.txt b/extras/modules/role-editor/data/default-capabilities.txt new file mode 100644 index 0000000..8464582 --- /dev/null +++ b/extras/modules/role-editor/data/default-capabilities.txt @@ -0,0 +1,61 @@ +activate_plugins +create_users +delete_others_pages +delete_others_posts +delete_pages +delete_plugins +delete_posts +delete_private_pages +delete_private_posts +delete_published_pages +delete_published_posts +delete_themes +delete_users +edit_dashboard +edit_files +edit_others_pages +edit_others_posts +edit_pages +edit_plugins +edit_posts +edit_private_pages +edit_private_posts +edit_published_pages +edit_published_posts +edit_theme_options +edit_themes +edit_users +export +import +install_plugins +install_themes +level_0 +level_1 +level_10 +level_2 +level_3 +level_4 +level_5 +level_6 +level_7 +level_8 +level_9 +list_users +manage_categories +manage_links +manage_options +moderate_comments +promote_users +publish_pages +publish_posts +read +read_private_pages +read_private_posts +remove_users +switch_themes +unfiltered_html +unfiltered_upload +update_core +update_plugins +update_themes +upload_files \ No newline at end of file diff --git a/extras/modules/role-editor/data/default-multisite-capabilities.txt b/extras/modules/role-editor/data/default-multisite-capabilities.txt new file mode 100644 index 0000000..a9e9fe7 --- /dev/null +++ b/extras/modules/role-editor/data/default-multisite-capabilities.txt @@ -0,0 +1,9 @@ +create_sites +delete_sites +manage_sites +manage_network +manage_network_users +manage_network_themes +manage_network_plugins +manage_network_options +upgrade_network \ No newline at end of file diff --git a/extras/modules/role-editor/data/default-role-capabilities-4.1.txt b/extras/modules/role-editor/data/default-role-capabilities-4.1.txt new file mode 100644 index 0000000..fc59dc3 --- /dev/null +++ b/extras/modules/role-editor/data/default-role-capabilities-4.1.txt @@ -0,0 +1,127 @@ +administrator + switch_themes + edit_themes + activate_plugins + edit_plugins + edit_users + edit_files + manage_options + moderate_comments + manage_categories + manage_links + upload_files + import + + //Note: On Multisite, only Super Admins have the unfiltered_html capability. + unfiltered_html + + edit_posts + edit_others_posts + edit_published_posts + publish_posts + edit_pages + read + level_10 + level_9 + level_8 + level_7 + level_6 + level_5 + level_4 + level_3 + level_2 + level_1 + level_0 + edit_others_pages + edit_published_pages + publish_pages + delete_pages + delete_others_pages + delete_published_pages + delete_posts + delete_others_posts + delete_published_posts + delete_private_posts + edit_private_posts + read_private_posts + delete_private_pages + edit_private_pages + read_private_pages + delete_users + create_users + unfiltered_upload + edit_dashboard + update_plugins + delete_plugins + install_plugins + update_themes + install_themes + update_core + list_users + remove_users + add_users + promote_users + edit_theme_options + delete_themes + export + +editor + moderate_comments + manage_categories + manage_links + upload_files + edit_posts + edit_others_posts + edit_published_posts + publish_posts + edit_pages + read + level_7 + level_6 + level_5 + level_4 + level_3 + level_2 + level_1 + level_0 + edit_others_pages + edit_published_pages + publish_pages + delete_pages + delete_others_pages + delete_published_pages + delete_posts + delete_others_posts + delete_published_posts + delete_private_posts + edit_private_posts + read_private_posts + delete_private_pages + edit_private_pages + read_private_pages + + //Note: On Multisite, only the Super Admin has unfiltered_html. + unfiltered_html + +author + upload_files + edit_posts + edit_published_posts + publish_posts + read + level_2 + level_1 + level_0 + delete_posts + delete_published_posts + +contributor + edit_posts + read + level_1 + level_0 + delete_posts + +subscriber + read + level_0 \ No newline at end of file diff --git a/extras/modules/role-editor/data/deprecated-capabilities.txt b/extras/modules/role-editor/data/deprecated-capabilities.txt new file mode 100644 index 0000000..6bbdcf3 --- /dev/null +++ b/extras/modules/role-editor/data/deprecated-capabilities.txt @@ -0,0 +1,12 @@ +level_0 +level_1 +level_10 +level_2 +level_3 +level_4 +level_5 +level_6 +level_7 +level_8 +level_9 +edit_files \ No newline at end of file diff --git a/extras/modules/role-editor/data/plugin-capabilities.csv b/extras/modules/role-editor/data/plugin-capabilities.csv new file mode 100644 index 0000000..0f336a0 --- /dev/null +++ b/extras/modules/role-editor/data/plugin-capabilities.csv @@ -0,0 +1,5482 @@ +0,len-slider,1000,Len Slider +Customer,wp-shop-original,3000,WP Shop +FlAG Add skins,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Change options,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Change skin,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Delete skins,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Import folder,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Manage banners,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Manage gallery,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Manage music,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Manage others gallery,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Manage video,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Upload images,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG Use TinyMCE,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG iFrame page,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +FlAG overview,flash-album-gallery,20000,Gallery – Flagallery Photo Portfolio +NextGEN Attach Interface,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Change options,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Change options,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Change style,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Change style,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Edit album,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Edit album,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Gallery overview,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Gallery overview,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Manage gallery,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Manage gallery,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Manage others gallery,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Manage others gallery,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Manage tags,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Manage tags,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Upload images,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Upload images,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +NextGEN Use TinyMCE,nextcellent-gallery-nextgen-legacy,20000,NextCellent Gallery – NextGEN Legacy +NextGEN Use TinyMCE,nextgen-gallery,900000,WordPress Gallery Plugin – NextGEN Gallery +Nginx Helper | Config,nginx-helper,60000,Nginx Helper +Nginx Helper | Purge cache,nginx-helper,60000,Nginx Helper +Show_Admin_Bar_in_Front,dd-roles,200,DD Roles +UM Configure Plugin,user-messages,400,User Messages +UM Ignore Public Msg,user-messages,400,User Messages +UM Receive Msg,user-messages,400,User Messages +UM Refuse Private Msg,user-messages,400,User Messages +UM Send Email Msg,user-messages,400,User Messages +UM Send Private Msg,user-messages,400,User Messages +UM Send Public Msg,user-messages,400,User Messages +UM Use Plugin,user-messages,400,User Messages +WP-CRM: Add User Messages,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: Change Color Scheme,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: Change Passwords,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: Change Role,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: Manage Settings,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: Perform Advanced Functions,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: Send Group Message,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: View Detailed Logs,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: View Messages,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: View Overview,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +WP-CRM: View Profiles,wp-crm,4000,WP-CRM – Customer Relations Management for WordPress +_debug_objects,debug-objects,800,Debug Objects +_wswebinar_changesettings,wp-webinarsystem,2000,WP WebinarSystem +_wswebinar_createwebinars,wp-webinarsystem,2000,WP WebinarSystem +_wswebinar_managequestions,wp-webinarsystem,2000,WP WebinarSystem +_wswebinar_managesubscribers,wp-webinarsystem,2000,WP WebinarSystem +access_automate_mautic,automate-mautic,400,AutomatePlug – Mautic for WordPress +access_gtmetrix,gtmetrix-for-wordpress,10000,GTmetrix for WordPress +access_kingcomposer,kingcomposer,70000,Page Builder: KingComposer – Free Drag and Drop page builder by King-Theme +access_mageewp_page_layout,mageewp-page-layout,3000,Mageewp Page Layout +access_masterslider,master-slider,100000,Master Slider – Responsive Touch Slider +access_s2member_level0,s2member,30000,"s2Member Framework (Member Roles, Capabilities, Membership, PayPal Members)" +access_s2member_level1,s2member,30000,"s2Member Framework (Member Roles, Capabilities, Membership, PayPal Members)" +access_s2member_level2,s2member,30000,"s2Member Framework (Member Roles, Capabilities, Membership, PayPal Members)" +access_s2member_level3,s2member,30000,"s2Member Framework (Member Roles, Capabilities, Membership, PayPal Members)" +access_s2member_level4,s2member,30000,"s2Member Framework (Member Roles, Capabilities, Membership, PayPal Members)" +access_server_browser,download-manager,100000,WordPress Download Manager +access_touchpoints,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +access_ukuupeoples,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +access_woocommerce_pos,woocommerce-pos,7000,WooCommerce POS +access_zopim,zopim-live-chat,90000,Zendesk Chat +activate_modules,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +activate_wordpoints_extensions,wordpoints,700,WordPoints +activate_wordpoints_modules,wordpoints,700,WordPoints +add-to-head,per-page-add-to,20000,Per page add to head +add_book-reviews,book-review-library,700,Book Review Library +add_discount_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +add_multiple_datasets,projectmanager,400,ProjectManager +add_users,wp-real-estate,400,WP Real Estate +add_yop_poll_votes,yop-poll,20000,YOP Poll +admin intel,intelligence,600,Intelligence +admin-post-highlights,post-highlights,200,post highlights +admin_albo,albo-pretorio-on-line,2000,Albo Pretorio On line +admin_simple_tags,simple-tags,100000,Simple Tags +admin_wp_cn_kit,wp-kit-cn,200,WP Kit CN +admin_wp_copy,copy-link,2000,CopyLink +admin_zerobs_customers,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_customers_tags,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_events,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_forms,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_invoices,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_logs_addedit,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_logs_delete,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_mailcampaigns,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_manage_options,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_notifications,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_quotes,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_sendemails_contacts,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_transactions,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_usr,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_view_customers,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_view_events,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_view_invoices,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_view_quotes,zero-bs-crm,1000,Zero BS WordPress CRM +admin_zerobs_view_transactions,zero-bs-crm,1000,Zero BS WordPress CRM +administer_awesome_support,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +administrator,advanced-access-manager,90000,Advanced Access Manager +administrator,developer-mode,1000,Developer Mode +adrotate_ad_delete,adrotate,50000,AdRotate Banner Manager +adrotate_ad_manage,adrotate,50000,AdRotate Banner Manager +adrotate_group_delete,adrotate,50000,AdRotate Banner Manager +adrotate_group_manage,adrotate,50000,AdRotate Banner Manager +advanced_ads_edit_ads,advanced-ads,70000,Advanced Ads – Ad Manager with AdSense Integration +advanced_ads_manage_options,advanced-ads,70000,Advanced Ads – Ad Manager with AdSense Integration +advanced_ads_manage_placements,advanced-ads,70000,Advanced Ads – Ad Manager with AdSense Integration +advanced_ads_place_ads,advanced-ads,70000,Advanced Ads – Ad Manager with AdSense Integration +advanced_ads_see_interface,advanced-ads,70000,Advanced Ads – Ad Manager with AdSense Integration +aff_access,affiliates,6000,Affiliates +aff_admin_affiliates,affiliates,6000,Affiliates +aff_admin_options,affiliates,6000,Affiliates +ag_manage_advanced,age-gate,7000,Age Gate +ag_manage_appearance,age-gate,7000,Age Gate +ag_manage_messaging,age-gate,7000,Age Gate +ag_manage_restrictions,age-gate,7000,Age Gate +ag_manage_set_content_bypass,age-gate,7000,Age Gate +ag_manage_set_content_restriction,age-gate,7000,Age Gate +ag_manage_set_custom_age,age-gate,7000,Age Gate +ag_manage_settings,age-gate,7000,Age Gate +aiosp_manage_seo,all-in-one-seo-pack,2000000,All in One SEO Pack +akp_edit_five,adkingpro,600,Ad King Pro +akp_edit_four,adkingpro,600,Ad King Pro +akp_edit_one,adkingpro,600,Ad King Pro +akp_edit_three,adkingpro,600,Ad King Pro +akp_edit_two,adkingpro,600,Ad King Pro +answer_chat,yith-live-chat,1000,YITH Live Chat +ap_approve_comment,anspress-question-answer,4000,AnsPress – Question and answer +ap_change_status,anspress-question-answer,4000,AnsPress – Question and answer +ap_change_status_other,anspress-question-answer,4000,AnsPress – Question and answer +ap_delete_answer,anspress-question-answer,4000,AnsPress – Question and answer +ap_delete_comment,anspress-question-answer,4000,AnsPress – Question and answer +ap_delete_others_answer,anspress-question-answer,4000,AnsPress – Question and answer +ap_delete_others_comment,anspress-question-answer,4000,AnsPress – Question and answer +ap_delete_others_question,anspress-question-answer,4000,AnsPress – Question and answer +ap_delete_post_permanent,anspress-question-answer,4000,AnsPress – Question and answer +ap_delete_question,anspress-question-answer,4000,AnsPress – Question and answer +ap_edit_answer,anspress-question-answer,4000,AnsPress – Question and answer +ap_edit_comment,anspress-question-answer,4000,AnsPress – Question and answer +ap_edit_others_answer,anspress-question-answer,4000,AnsPress – Question and answer +ap_edit_others_comment,anspress-question-answer,4000,AnsPress – Question and answer +ap_edit_others_question,anspress-question-answer,4000,AnsPress – Question and answer +ap_edit_question,anspress-question-answer,4000,AnsPress – Question and answer +ap_new_answer,anspress-question-answer,4000,AnsPress – Question and answer +ap_new_comment,anspress-question-answer,4000,AnsPress – Question and answer +ap_new_question,anspress-question-answer,4000,AnsPress – Question and answer +ap_no_moderation,anspress-question-answer,4000,AnsPress – Question and answer +ap_read_answer,anspress-question-answer,4000,AnsPress – Question and answer +ap_read_comment,anspress-question-answer,4000,AnsPress – Question and answer +ap_read_question,anspress-question-answer,4000,AnsPress – Question and answer +ap_restore_posts,anspress-question-answer,4000,AnsPress – Question and answer +ap_toggle_best_answer,anspress-question-answer,4000,AnsPress – Question and answer +ap_toggle_featured,anspress-question-answer,4000,AnsPress – Question and answer +ap_upload_cover,anspress-question-answer,4000,AnsPress – Question and answer +ap_view_moderate,anspress-question-answer,4000,AnsPress – Question and answer +ap_view_private,anspress-question-answer,4000,AnsPress – Question and answer +ap_vote_close,anspress-question-answer,4000,AnsPress – Question and answer +ap_vote_down,anspress-question-answer,4000,AnsPress – Question and answer +ap_vote_flag,anspress-question-answer,4000,AnsPress – Question and answer +ap_vote_up,anspress-question-answer,4000,AnsPress – Question and answer +armember,armember-membership,200,ARMember – Content Restriction & Membership Plugin +asa1_delete_collections,amazonsimpleadmin,6000,AmazonSimpleAdmin +asa1_edit_cache,amazonsimpleadmin,6000,AmazonSimpleAdmin +asa1_edit_collections,amazonsimpleadmin,6000,AmazonSimpleAdmin +asa1_edit_options,amazonsimpleadmin,6000,AmazonSimpleAdmin +asa1_edit_setup,amazonsimpleadmin,6000,AmazonSimpleAdmin +assign_achievement_events,achievements,300,Achievements for WordPress +assign_ad_terms,apply-online,5000,Apply Online +assign_agent_terms,essential-real-estate,3000,Essential Real Estate +assign_archive_structure,archive,700,Archive +assign_awebooking_terms,awebooking,6000,AweBooking – Hotel Booking System +assign_book_terms,novelist,800,Novelist +assign_campaign_terms,charitable,10000,Charitable – Donation Plugin +assign_cannedresponse_category,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +assign_cannedresponse_tag,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +assign_car_listing_terms,wp-car-manager,3000,WP Car Manager +assign_classified_listing_terms,classifieds-wp,800,Classifieds WP +assign_client_terms,upstream,1000,WordPress Project Management by UpStream +assign_contact_country,wp-easy-contact,600,Best Contact Management Software for WordPress +assign_contact_state,wp-easy-contact,600,Best Contact Management Software for WordPress +assign_contact_tag,wp-easy-contact,600,Best Contact Management Software for WordPress +assign_contact_topic,wp-easy-contact,600,Best Contact Management Software for WordPress +assign_course_cats,lifterlms,8000,LifterLMS +assign_course_difficulties,lifterlms,8000,LifterLMS +assign_course_tags,lifterlms,8000,LifterLMS +assign_course_tracks,lifterlms,8000,LifterLMS +assign_cover_artist_terms,mooberry-book-manager,1000,Mooberry Book Manager +assign_customfields,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +assign_departments,employee-directory,400,Staff Directory – Employee Directory for WordPress +assign_editor_terms,mooberry-book-manager,1000,Mooberry Book Manager +assign_email_categories,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +assign_employee_tags,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +assign_employment_type,employee-directory,400,Staff Directory – Employee Directory for WordPress +assign_event_listing_terms,wp-event-manager,1000,WP Event Manager +assign_event_magic_terms,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +assign_everest_form_terms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +assign_fa_terms,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +assign_feed_source_terms,wp-rss-aggregator,60000,WP RSS Aggregator +assign_feed_terms,wp-rss-aggregator,60000,WP RSS Aggregator +assign_food_group_terms,restaurantpress,3000,RestaurantPress +assign_food_menu_terms,restaurantpress,3000,RestaurantPress +assign_gender,employee-directory,400,Staff Directory – Employee Directory for WordPress +assign_genre_terms,mooberry-book-manager,1000,Mooberry Book Manager +assign_give_form_terms,give,50000,Give – Donation Plugin and Fundraising Platform +assign_give_payment_terms,give,50000,Give – Donation Plugin and Fundraising Platform +assign_groups,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +assign_hotel_location_terms,awebooking,6000,AweBooking – Hotel Booking System +assign_hotel_service_terms,awebooking,6000,AweBooking – Hotel Booking System +assign_illustrator_terms,mooberry-book-manager,1000,Mooberry Book Manager +assign_invoice_terms,essential-real-estate,3000,Essential Real Estate +assign_jbbrd_businesses_tags,job-board,300,Job Board by BestWebSoft +assign_jbbrd_employment_tags,job-board,300,Job Board by BestWebSoft +assign_job_listing_terms,wp-job-manager,100000,WP Job Manager +assign_jobtitles,employee-directory,400,Staff Directory – Employee Directory for WordPress +assign_klaviyo_shop_cart_terms,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +assign_listing_terms,wpcasa,2000,WPCasa +assign_marital_status,employee-directory,400,Staff Directory – Employee Directory for WordPress +assign_membership_cats,lifterlms,8000,LifterLMS +assign_membership_tags,lifterlms,8000,LifterLMS +assign_mp_menu_item_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +assign_mprm_order_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +assign_office_locations,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +assign_opalestate_agents_terms,opal-estate,1000,Opal Estate +assign_opalestate_properties_terms,opal-estate,1000,Opal Estate +assign_package_terms,essential-real-estate,3000,Essential Real Estate +assign_person_area,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +assign_person_location,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +assign_person_rareas,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +assign_person_title,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +assign_portfolio_categories,custom-content-portfolio,1000,Custom Content Portfolio +assign_portfolio_tags,custom-content-portfolio,1000,Custom Content Portfolio +assign_portfolio_terms,flash-toolkit,30000,Flash Toolkit +assign_portfolio_terms,suffice-toolkit,5000,Suffice Toolkit +assign_pricing_rate_terms,awebooking,6000,AweBooking – Hotel Booking System +assign_product_categories,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +assign_product_categories,post-type-x,1000,Product Catalog X +assign_product_terms,dc-woocommerce-multi-vendor,10000,WC Marketplace +assign_product_terms,easy-digital-downloads,60000,Easy Digital Downloads +assign_product_terms,jigoshop,4000,Jigoshop +assign_product_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +assign_product_terms,webmaster-user-role,8000,Webmaster User Role +assign_product_terms,woocommerce,4000000,WooCommerce +assign_product_terms,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +assign_project_terms,upstream,1000,WordPress Project Management by UpStream +assign_property_terms,essential-real-estate,3000,Essential Real Estate +assign_quote_authors,mg-quotes,300,mg Quotes +assign_quote_categories,mg-quotes,300,mg Quotes +assign_raq_services,request-a-quote,1000,Request a Quote +assign_resume_organizations,wp-resume,700,WP Resume +assign_resume_sections,wp-resume,700,WP Resume +assign_room_reservation_terms,wp-hotelier,1000,Easy WP Hotelier +assign_room_terms,wp-hotelier,1000,Easy WP Hotelier +assign_room_type_terms,awebooking,6000,AweBooking – Hotel Booking System +assign_series_terms,mooberry-book-manager,1000,Mooberry Book Manager +assign_shop_coupon_terms,jigoshop,4000,Jigoshop +assign_shop_coupon_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +assign_shop_coupon_terms,webmaster-user-role,8000,Webmaster User Role +assign_shop_coupon_terms,woocommerce,4000000,WooCommerce +assign_shop_discount_terms,easy-digital-downloads,60000,Easy Digital Downloads +assign_shop_email_terms,jigoshop,4000,Jigoshop +assign_shop_email_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +assign_shop_order_terms,jigoshop,4000,Jigoshop +assign_shop_order_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +assign_shop_order_terms,webmaster-user-role,8000,Webmaster User Role +assign_shop_order_terms,woocommerce,4000000,WooCommerce +assign_shop_payment_terms,easy-digital-downloads,60000,Easy Digital Downloads +assign_shop_webhook_terms,woocommerce,4000000,WooCommerce +assign_sp_calendar_terms,sportspress,20000,SportsPress – Sports Club & League Manager +assign_sp_config_terms,sportspress,20000,SportsPress – Sports Club & League Manager +assign_sp_event_terms,sportspress,20000,SportsPress – Sports Club & League Manager +assign_sp_list_terms,sportspress,20000,SportsPress – Sports Club & League Manager +assign_sp_player_terms,sportspress,20000,SportsPress – Sports Club & League Manager +assign_sp_staff_terms,sportspress,20000,SportsPress – Sports Club & League Manager +assign_sp_table_terms,sportspress,20000,SportsPress – Sports Club & League Manager +assign_sp_team_terms,sportspress,20000,SportsPress – Sports Club & League Manager +assign_tag_terms,mooberry-book-manager,1000,Mooberry Book Manager +assign_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +assign_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +assign_ticket_creator,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +assign_ticket_priority,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +assign_ticket_status,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +assign_ticket_topic,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +assign_topic_tags,bbpress,300000,bbPress +assign_trans_log_terms,essential-real-estate,3000,Essential Real Estate +assign_user_package_terms,essential-real-estate,3000,Essential Real Estate +assign_user_registration_terms,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +assign_wctrl_contents,widgets-control,1000,Widgets Control +assign_wd_ads_groups,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +assign_wpcm_club_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +assign_wpcm_match_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +assign_wpcm_player_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +assign_wpcm_sponsor_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +assign_wpcm_staff_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +assign_wpsdeals_terms,deals-engine,200,Social Deals Engine +assign_wpsdealssales_terms,deals-engine,200,Social Deals Engine +attach_files,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +attach_files,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +avartan_slider_access,avartan-slider-lite,2000,Responsive WordPress Slider – Avartan Slider Lite +avh_fdas_admin,avh-first-defense-against-spam,7000,AVH First Defense Against Spam +backwpup,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_backups,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_backups_delete,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_backups_download,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_jobs,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_jobs_edit,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_jobs_start,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_logs,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_logs_delete,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_restore,backwpup,600000,BackWPup – WordPress Backup Plugin +backwpup_settings,backwpup,600000,BackWPup – WordPress Backup Plugin +become_yop_poll_pro,yop-poll,20000,YOP Poll +birthdays_list,birthdays-widget,2000,Birthdays Widget +bnfw,bnfw,20000,Better Notifications for WordPress +bookacti_create_activities,booking-activities,900,Booking Activities +bookacti_create_bookings,booking-activities,900,Booking Activities +bookacti_create_forms,booking-activities,900,Booking Activities +bookacti_create_templates,booking-activities,900,Booking Activities +bookacti_delete_activities,booking-activities,900,Booking Activities +bookacti_delete_bookings,booking-activities,900,Booking Activities +bookacti_delete_forms,booking-activities,900,Booking Activities +bookacti_delete_templates,booking-activities,900,Booking Activities +bookacti_edit_activities,booking-activities,900,Booking Activities +bookacti_edit_bookings,booking-activities,900,Booking Activities +bookacti_edit_forms,booking-activities,900,Booking Activities +bookacti_edit_templates,booking-activities,900,Booking Activities +bookacti_manage_booking_activities,booking-activities,900,Booking Activities +bookacti_manage_booking_activities_settings,booking-activities,900,Booking Activities +bookacti_manage_bookings,booking-activities,900,Booking Activities +bookacti_manage_forms,booking-activities,900,Booking Activities +bookacti_manage_templates,booking-activities,900,Booking Activities +bookacti_read_templates,booking-activities,900,Booking Activities +brightcove_get_user_default_account,brightcove-video-connect,900,Brightcove Video Connect +brightcove_manipulate_accounts,brightcove-video-connect,900,Brightcove Video Connect +brightcove_manipulate_playlists,brightcove-video-connect,900,Brightcove Video Connect +brightcove_manipulate_videos,brightcove-video-connect,900,Brightcove Video Connect +brightcove_set_site_default_account,brightcove-video-connect,900,Brightcove Video Connect +brightcove_set_user_default_account,brightcove-video-connect,900,Brightcove Video Connect +brizy_edit_whole_page,brizy,30000,Brizy – Page Builder +btev,bluetrait-event-viewer,800,BTEV +bug_assigned_to_field,upstream,1000,WordPress Project Management by UpStream +bug_description_field,upstream,1000,WordPress Project Management by UpStream +bug_due_date_field,upstream,1000,WordPress Project Management by UpStream +bug_file_field,upstream,1000,WordPress Project Management by UpStream +bug_severity_field,upstream,1000,WordPress Project Management by UpStream +bug_status_field,upstream,1000,WordPress Project Management by UpStream +bug_title_field,upstream,1000,WordPress Project Management by UpStream +bulk_edit_roles,wpfront-user-role-editor,60000,WPFront User Role Editor +buwd_api_keys,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_backups,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_backups_delete,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_backups_download,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_edit,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_job,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_job_delete,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_job_edit,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_job_run,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_log_delete,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_log_download,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_log_view,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_logs,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_settings,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_settings_export,backup-wd,8000,Backup WD – Backup and Restore Plugin +buwd_settings_import,backup-wd,8000,Backup WD – Backup and Restore Plugin +buy_tax_free,pricing-deals-for-woocommerce,7000,Pricing Deals for WooCommerce +buy_wholesale,pricing-deals-for-woocommerce,7000,Pricing Deals for WooCommerce +cb_parallax_edit,cb-parallax,400,cbParallax +cbe_edit_background,custom-background-extended,3000,Custom Background Extended +cdi_gateway,colissimo-delivery-integration,2000,Colissimo Delivery Integration +cfdb7_access,contact-form-cfdb7,100000,Contact Form 7 Database Addon – CFDB7 +challonge_report_own,challonge,900,Challonge +challonge_signup,challonge,900,Challonge +challonge_view,challonge,900,Challonge +chatbro_ban_user,chatbro,1000,Chat Bro – Chat linked with Telegram or VK chat +chatbro_delete_message,chatbro,1000,Chat Bro – Chat linked with Telegram or VK chat +chatbro_view_chat,chatbro,1000,Chat Bro – Chat linked with Telegram or VK chat +che_edit_header,custom-header-extended,5000,Custom Header Extended +check_PBot,proofread-bot,400,Proofread Bot +chimpmate_cap,chimpmate,3000,ChimpMate – WordPress MailChimp Assistant +chronosly_author,chronosly-events-calendar,4000,Chronosly Events Calendar +chronosly_license,chronosly-events-calendar,4000,Chronosly Events Calendar +chronosly_options,chronosly-events-calendar,4000,Chronosly Events Calendar +clear_errors,timber,400,Timber +clone_own_yop_polls,yop-poll,20000,YOP Poll +clone_own_yop_polls_templates,yop-poll,20000,YOP Poll +clone_yop_polls,yop-poll,20000,YOP Poll +clone_yop_polls_templates,yop-poll,20000,YOP Poll +close_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +close_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +config_postie,postie,20000,Postie +configure_recent_dash_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +configure_recent_tickets_dashboard,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +connections_add_entry,connections,10000,Connections Business Directory +connections_add_entry_moderated,connections,10000,Connections Business Directory +connections_change_roles,connections,10000,Connections Business Directory +connections_change_settings,connections,10000,Connections Business Directory +connections_delete_entry,connections,10000,Connections Business Directory +connections_edit_categories,connections,10000,Connections Business Directory +connections_edit_entry,connections,10000,Connections Business Directory +connections_edit_entry_moderated,connections,10000,Connections Business Directory +connections_manage,connections,10000,Connections Business Directory +connections_manage_template,connections,10000,Connections Business Directory +connections_view_dashboard,connections,10000,Connections Business Directory +connections_view_menu,connections,10000,Connections Business Directory +connections_view_private,connections,10000,Connections Business Directory +connections_view_public,connections,10000,Connections Business Directory +connections_view_unlisted,connections,10000,Connections Business Directory +copy_posts,duplicate-post,2000000,Duplicate Post +copy_posts,project-panorama-lite,1000,Project Panorama +copy_posts,project-status,200,Project Status +cp_add_projects,collabpress,700,CollabPress +cp_add_task,collabpress,700,CollabPress +cp_add_task_lists,collabpress,700,CollabPress +cp_edit_projects,collabpress,700,CollabPress +cp_edit_task,collabpress,700,CollabPress +cp_edit_task_lists,collabpress,700,CollabPress +cp_view_category_permalinks,custom-permalinks,100000,Custom Permalinks +cp_view_post_permalinks,custom-permalinks,100000,Custom Permalinks +create_admincolorschemes,easy-admin-color-schemes,1000,Easy Admin Color Schemes +create_applications,apply-online,5000,Apply Online +create_blocks,gutenberg,500000,Gutenberg +create_courses,lifterlms,8000,LifterLMS +create_edit_projects,ignitiondeck,2000,IgnitionDeck Crowdfunding & Commerce +create_fep_announcements,front-end-pm,8000,Front End PM +create_fep_messages,front-end-pm,8000,Front End PM +create_forms,formlift,800,FormLift for Infusionsoft Web Forms +create_forms,pronamic-ideal,6000,Pronamic Pay +create_galleries,gallery-box,2000,Gallery Box +create_glossaries,glossary-by-codeat,1000,Glossary +create_lazyest_folder,lazyest-gallery,1000,Lazyest Gallery +create_lessons,lifterlms,8000,LifterLMS +create_masterslider,master-slider,100000,Master Slider – Responsive Touch Slider +create_memberships,lifterlms,8000,LifterLMS +create_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +create_niso_slider_carousels,niso-carousel,200,Niso Carousel +create_niso_slider_carousels,niso-carousel-slider,400,Niso Carousel Slider +create_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +create_posts,wp2appir,300,wp2appir +create_posts,zero-bs-crm,1000,Zero BS WordPress CRM +create_questions,lifterlms,8000,LifterLMS +create_quizzes,lifterlms,8000,LifterLMS +create_restaurant_items,restaurant,300,Restaurant +create_roles,members,100000,Members +create_roles,wpfront-user-role-editor,60000,WPFront User Role Editor +create_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +create_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +create_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +create_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +create_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +create_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +create_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +create_touchpoints,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +create_ukuupeoples,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +create_whistles,whistles,2000,Whistles +create_wpp_properties,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +create_wpse_profiles,wp-smart-editor,900,WP Smart Editor +crm_role,wsdesk,1000,WSDesk – WordPress HelpDesk & Support Ticket System +cstm_cds_full_access,custom-codes,800,Custom Codes +cuar_access_admin_panel,customer-area,10000,WP Customer Area +cuar_edit_account,customer-area,10000,WP Customer Area +cuar_pf_assign_categories,customer-area,10000,WP Customer Area +cuar_pf_delete,customer-area,10000,WP Customer Area +cuar_pf_delete_categories,customer-area,10000,WP Customer Area +cuar_pf_edit,customer-area,10000,WP Customer Area +cuar_pf_edit_categories,customer-area,10000,WP Customer Area +cuar_pf_list_all,customer-area,10000,WP Customer Area +cuar_pf_manage_attachments,customer-area,10000,WP Customer Area +cuar_pf_manage_categories,customer-area,10000,WP Customer Area +cuar_pf_read,customer-area,10000,WP Customer Area +cuar_pp_assign_categories,customer-area,10000,WP Customer Area +cuar_pp_delete,customer-area,10000,WP Customer Area +cuar_pp_delete_categories,customer-area,10000,WP Customer Area +cuar_pp_edit,customer-area,10000,WP Customer Area +cuar_pp_edit_categories,customer-area,10000,WP Customer Area +cuar_pp_list_all,customer-area,10000,WP Customer Area +cuar_pp_manage_categories,customer-area,10000,WP Customer Area +cuar_pp_read,customer-area,10000,WP Customer Area +cuar_view_account,customer-area,10000,WP Customer Area +cuar_view_any_cuar_private_file,customer-area,10000,WP Customer Area +cuar_view_any_cuar_private_page,customer-area,10000,WP Customer Area +cuar_view_files,customer-area,10000,WP Customer Area +cuar_view_pages,customer-area,10000,WP Customer Area +cuar_view_top_bar,customer-area,10000,WP Customer Area +customize_admin,client-dash,6000,Client Dash +deactivate_modules,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +debug intel,intelligence,600,Intelligence +del_leagues,leaguemanager,2000,LeagueManager +del_matches,leaguemanager,2000,LeagueManager +del_seasons,leaguemanager,2000,LeagueManager +del_teams,leaguemanager,2000,LeagueManager +delete all intel visitors,intelligence,600,Intelligence +delete_acadp_field,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_acadp_listing,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_acadp_payment,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_achievement_events,achievements,300,Achievements for WordPress +delete_achievement_progresses,achievements,300,Achievements for WordPress +delete_achievements,achievements,300,Achievements for WordPress +delete_ad_terms,apply-online,5000,Apply Online +delete_admincolorschemes,easy-admin-color-schemes,1000,Easy Admin Color Schemes +delete_ads,apply-online,5000,Apply Online +delete_aec_event,another-events-calendar,800,Another Events Calendar +delete_aec_events,another-events-calendar,800,Another Events Calendar +delete_aec_organizer,another-events-calendar,800,Another Events Calendar +delete_aec_organizers,another-events-calendar,800,Another Events Calendar +delete_aec_venue,another-events-calendar,800,Another Events Calendar +delete_aec_venues,another-events-calendar,800,Another Events Calendar +delete_affiliate_keywords,affiliate,700,Affiliate +delete_agent,essential-real-estate,3000,Essential Real Estate +delete_agent_terms,essential-real-estate,3000,Essential Real Estate +delete_agents,essential-real-estate,3000,Essential Real Estate +delete_aggregator-records,the-events-calendar,700000,The Events Calendar +delete_ai1ec_event,all-in-one-event-calendar,100000,All-in-One Event Calendar +delete_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +delete_aiovg_video,all-in-one-video-gallery,1000,All-in-One Video Gallery +delete_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +delete_all_touchpoints,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +delete_all_ukuupeoples,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +delete_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +delete_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +delete_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +delete_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +delete_anbs,alert-notice-boxes,1000,Alert Notice Boxes +delete_applications,apply-online,5000,Apply Online +delete_archiv,archive,700,Archive +delete_archive_structure,archive,700,Archive +delete_archivs,archive,700,Archive +delete_article,issuem,1000,IssueM +delete_articles,issuem,1000,IssueM +delete_at_biz_dir,directorist,500,Directorist – Business Directory Plugin +delete_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +delete_atbdp_order,directorist,500,Directorist – Business Directory Plugin +delete_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +delete_attachments,wpfront-user-role-editor,60000,WPFront User Role Editor +delete_attachments,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +delete_attendees_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_awebooking,awebooking,6000,AweBooking – Hotel Booking System +delete_awebooking_terms,awebooking,6000,AweBooking – Hotel Booking System +delete_awebookings,awebooking,6000,AweBooking – Hotel Booking System +delete_birs_appointment,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_client,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_location,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_payment,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_service,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_staff,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_blocks,gutenberg,500000,Gutenberg +delete_board_committees,nonprofit-board-management,400,Nonprofit Board Management +delete_board_events,nonprofit-board-management,400,Nonprofit Board Management +delete_book,novelist,800,Novelist +delete_book-reviews,book-review-library,700,Book Review Library +delete_book_terms,novelist,800,Novelist +delete_books,novelist,800,Novelist +delete_box,boxzilla,20000,Boxzilla +delete_bps_forms,bp-profile-search,10000,BP Profile Search +delete_calp_event,calpress-event-calendar,5000,CalPress Calendar +delete_calp_events,calpress-event-calendar,5000,CalPress Calendar +delete_campaign,charitable,10000,Charitable – Donation Plugin +delete_campaign,leyka,1000,Leyka +delete_campaign_terms,charitable,10000,Charitable – Donation Plugin +delete_campaigns,charitable,10000,Charitable – Donation Plugin +delete_campaigns,leyka,1000,Leyka +delete_cannedresponse_category,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_cannedresponse_tag,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_car_listing,wp-car-manager,3000,WP Car Manager +delete_car_listing_terms,wp-car-manager,3000,WP Car Manager +delete_car_listings,wp-car-manager,3000,WP Car Manager +delete_cbxaccounting,cbxwpsimpleaccounting,300,CBX Accounting +delete_cctor_coupon,coupon-creator,10000,Coupon Creator +delete_cctor_coupons,coupon-creator,10000,Coupon Creator +delete_checkins_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_chronosly,chronosly-events-calendar,4000,Chronosly Events Calendar +delete_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +delete_classified_listing,classifieds-wp,800,Classifieds WP +delete_classified_listing_terms,classifieds-wp,800,Classifieds WP +delete_classified_listings,classifieds-wp,800,Classifieds WP +delete_client,upstream,1000,WordPress Project Management by UpStream +delete_client_terms,upstream,1000,WordPress Project Management by UpStream +delete_clients,upstream,1000,WordPress Project Management by UpStream +delete_contact_country,wp-easy-contact,600,Best Contact Management Software for WordPress +delete_contact_state,wp-easy-contact,600,Best Contact Management Software for WordPress +delete_contact_tag,wp-easy-contact,600,Best Contact Management Software for WordPress +delete_contact_topic,wp-easy-contact,600,Best Contact Management Software for WordPress +delete_content_shortcodes,wpfront-user-role-editor,60000,WPFront User Role Editor +delete_course,lifterlms,8000,LifterLMS +delete_course_cats,lifterlms,8000,LifterLMS +delete_course_difficulties,lifterlms,8000,LifterLMS +delete_course_tags,lifterlms,8000,LifterLMS +delete_course_tracks,lifterlms,8000,LifterLMS +delete_courses,lifterlms,8000,LifterLMS +delete_cupri_pay,pardakht-delkhah,1000,پلاگین پرداخت دلخواه +delete_custom_css,custom-css-js,100000,Simple Custom CSS and JS +delete_custom_csss,custom-css-js,100000,Simple Custom CSS and JS +delete_customfields,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +delete_datasets,projectmanager,400,ProjectManager +delete_departments,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_ditty_news_ticker,ditty-news-ticker,40000,Ditty News Ticker +delete_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +delete_documents,wp-document-revisions,4000,WP Document Revisions +delete_donation,charitable,10000,Charitable – Donation Plugin +delete_donation,leyka,1000,Leyka +delete_donations,charitable,10000,Charitable – Donation Plugin +delete_donations,leyka,1000,Leyka +delete_dsn_note,admin-dashboard-site-notes,3000,Dashboard Site Notes +delete_dsn_notes,admin-dashboard-site-notes,3000,Dashboard Site Notes +delete_edr_course,educator,1000,Educator 2 +delete_edr_courses,educator,1000,Educator 2 +delete_edr_lesson,educator,1000,Educator 2 +delete_edr_lessons,educator,1000,Educator 2 +delete_edr_membership,educator,1000,Educator 2 +delete_edr_memberships,educator,1000,Educator 2 +delete_email_categories,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +delete_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +delete_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +delete_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +delete_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_emd_quotes,request-a-quote,1000,Request a Quote +delete_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_employee_tags,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +delete_employment_type,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_epa_albums,easy-photo-album,5000,Easy Photo Album +delete_event_categories,events-manager,100000,Events Manager +delete_event_listing,wp-event-manager,1000,WP Event Manager +delete_event_listing_terms,wp-event-manager,1000,WP Event Manager +delete_event_listings,wp-event-manager,1000,WP Event Manager +delete_event_magic,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +delete_event_magic_terms,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +delete_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +delete_events,event-organiser,40000,Event Organiser +delete_events,events-maker,4000,Events Maker by dFactory +delete_events,events-manager,100000,Events Manager +delete_events,quick-event-manager,5000,Quick Event Manager +delete_everest_form,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +delete_everest_form_terms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +delete_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +delete_fa_items,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +delete_fa_terms,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +delete_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +delete_feed,wp-rss-aggregator,60000,WP RSS Aggregator +delete_feed_source,wp-rss-aggregator,60000,WP RSS Aggregator +delete_feed_source_terms,wp-rss-aggregator,60000,WP RSS Aggregator +delete_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +delete_feed_terms,wp-rss-aggregator,60000,WP RSS Aggregator +delete_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +delete_fep_announcements,front-end-pm,8000,Front End PM +delete_fep_messages,front-end-pm,8000,Front End PM +delete_flexible_invoice,flexible-invoices,1000,Flexible Invoices for WordPress +delete_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +delete_food_group,restaurantpress,3000,RestaurantPress +delete_food_group_terms,restaurantpress,3000,RestaurantPress +delete_food_groups,restaurantpress,3000,RestaurantPress +delete_food_menu,restaurantpress,3000,RestaurantPress +delete_food_menu_terms,restaurantpress,3000,RestaurantPress +delete_food_menus,restaurantpress,3000,RestaurantPress +delete_form,formlift,800,FormLift for Infusionsoft Web Forms +delete_form,pronamic-ideal,6000,Pronamic Pay +delete_forms,pronamic-ideal,6000,Pronamic Pay +delete_forums,bbpress,300000,bbPress +delete_galleries,gallery-box,2000,Gallery Box +delete_gallery,gallery-box,2000,Gallery Box +delete_game,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_gender,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_give_form,give,50000,Give – Donation Plugin and Fundraising Platform +delete_give_form_terms,give,50000,Give – Donation Plugin and Fundraising Platform +delete_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +delete_give_payment,give,50000,Give – Donation Plugin and Fundraising Platform +delete_give_payment_terms,give,50000,Give – Donation Plugin and Fundraising Platform +delete_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +delete_glossaries,glossary-by-codeat,1000,Glossary +delete_glossary,glossary-by-codeat,1000,Glossary +delete_groups,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +delete_hanaboard-post,hana-board,1000,Hana-Board 하나보드 워드프레스 게시판 +delete_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +delete_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +delete_hf_membership_plan,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_hf_user_membership,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_hotel_location,awebooking,6000,AweBooking – Hotel Booking System +delete_hotel_location_terms,awebooking,6000,AweBooking – Hotel Booking System +delete_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +delete_hotel_service,awebooking,6000,AweBooking – Hotel Booking System +delete_hotel_service_terms,awebooking,6000,AweBooking – Hotel Booking System +delete_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +delete_ib_edu_membership,ibeducator,1000,Educator +delete_ib_edu_memberships,ibeducator,1000,Educator +delete_ib_educator_course,ibeducator,1000,Educator +delete_ib_educator_courses,ibeducator,1000,Educator +delete_ib_educator_lesson,ibeducator,1000,Educator +delete_ib_educator_lessons,ibeducator,1000,Educator +delete_ims_gallery,image-store,900,Image Store +delete_ims_gallerys,image-store,900,Image Store +delete_inbound-form,cta,10000,WordPress Calls to Action +delete_inbound-form,landing-pages,10000,WordPress Landing Pages +delete_inbound-form,leads,7000,WordPress Leads +delete_inbound-forms,cta,10000,WordPress Calls to Action +delete_inbound-forms,landing-pages,10000,WordPress Landing Pages +delete_inbound-forms,leads,7000,WordPress Leads +delete_invoice,essential-real-estate,3000,Essential Real Estate +delete_invoice_terms,essential-real-estate,3000,Essential Real Estate +delete_invoices,essential-real-estate,3000,Essential Real Estate +delete_item,gamipress,2000,GamiPress +delete_items,gamipress,2000,GamiPress +delete_jbbrd_businesses_tags,job-board,300,Job Board by BestWebSoft +delete_jbbrd_employment_tags,job-board,300,Job Board by BestWebSoft +delete_job_listing,wp-job-manager,100000,WP Job Manager +delete_job_listing_terms,wp-job-manager,100000,WP Job Manager +delete_job_listings,wp-job-manager,100000,WP Job Manager +delete_jobtitles,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_jscp_match,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_jscp_player,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_jscp_team,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_klaviyo_shop_cart,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +delete_klaviyo_shop_cart_terms,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +delete_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +delete_landing_pages,landing-pages,10000,WordPress Landing Pages +delete_language,sublanguage,1000,Sublanguage +delete_leads,cta,10000,WordPress Calls to Action +delete_leads,landing-pages,10000,WordPress Landing Pages +delete_leads,leads,7000,WordPress Leads +delete_legalpack_pages,legalpack,400,Legalpack +delete_lesson,lifterlms,8000,LifterLMS +delete_lessons,lifterlms,8000,LifterLMS +delete_listing,auto-listings,400,Auto Listings +delete_listing,wp-real-estate,400,WP Real Estate +delete_listing,wpcasa,2000,WPCasa +delete_listing_terms,wpcasa,2000,WPCasa +delete_listings,auto-listings,400,Auto Listings +delete_listings,wp-real-estate,400,WP Real Estate +delete_listings,wpcasa,2000,WPCasa +delete_locations,events-manager,100000,Events Manager +delete_login_redirects,wpfront-user-role-editor,60000,WPFront User Role Editor +delete_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_marital_status,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_masterslider,master-slider,100000,Master Slider – Responsive Touch Slider +delete_mbdb_book,mooberry-book-manager,1000,Mooberry Book Manager +delete_mbdb_book_grid,mooberry-book-manager,1000,Mooberry Book Manager +delete_mbdb_book_grids,mooberry-book-manager,1000,Mooberry Book Manager +delete_mbdb_books,mooberry-book-manager,1000,Mooberry Book Manager +delete_meals,restaurant-manager,800,Restaurant Manager +delete_membership,lifterlms,8000,LifterLMS +delete_membership_cats,lifterlms,8000,LifterLMS +delete_membership_tags,lifterlms,8000,LifterLMS +delete_memberships,lifterlms,8000,LifterLMS +delete_mp_menu_item,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_mp_menu_item_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_mprm_order,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_mprm_order_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_nc_reference,nelio-content,7000,Nelio Content – Social Media Marketing Automation +delete_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +delete_nemus-sliders,nemus-slider,2000,Nemus Slider +delete_news,news-manager,3000,News Manager +delete_newsletters,alo-easymail,10000,ALO EasyMail Newsletter +delete_niso_slider_carousel,niso-carousel,200,Niso Carousel +delete_niso_slider_carousel,niso-carousel-slider,400,Niso Carousel Slider +delete_niso_slider_carousels,niso-carousel,200,Niso Carousel +delete_niso_slider_carousels,niso-carousel-slider,400,Niso Carousel Slider +delete_office_locations,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +delete_opalestate_agents,opal-estate,1000,Opal Estate +delete_opalestate_agents_terms,opal-estate,1000,Opal Estate +delete_opalestate_agentss,opal-estate,1000,Opal Estate +delete_opalestate_properties,opal-estate,1000,Opal Estate +delete_opalestate_properties_terms,opal-estate,1000,Opal Estate +delete_opalestate_propertiess,opal-estate,1000,Opal Estate +delete_opanda-item,opt-in-panda,3000,OnePress Opt-In Panda +delete_opanda-item,social-locker,10000,OnePress Social Locker +delete_orbis_company,orbis,200,Orbis +delete_orbis_project,orbis,200,Orbis +delete_other_datasets,projectmanager,400,ProjectManager +delete_other_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +delete_other_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +delete_others_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_others_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_others_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_others_achievement_progresses,achievements,300,Achievements for WordPress +delete_others_achievements,achievements,300,Achievements for WordPress +delete_others_admincolorschemes,easy-admin-color-schemes,1000,Easy Admin Color Schemes +delete_others_ads,apply-online,5000,Apply Online +delete_others_aec_events,another-events-calendar,800,Another Events Calendar +delete_others_aec_organizers,another-events-calendar,800,Another Events Calendar +delete_others_aec_venues,another-events-calendar,800,Another Events Calendar +delete_others_affiliate_keywords,affiliate,700,Affiliate +delete_others_agents,essential-real-estate,3000,Essential Real Estate +delete_others_aggregator-records,the-events-calendar,700000,The Events Calendar +delete_others_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +delete_others_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +delete_others_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +delete_others_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +delete_others_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +delete_others_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +delete_others_anbs,alert-notice-boxes,1000,Alert Notice Boxes +delete_others_applications,apply-online,5000,Apply Online +delete_others_archivs,archive,700,Archive +delete_others_articles,issuem,1000,IssueM +delete_others_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +delete_others_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +delete_others_attachments,wpfront-user-role-editor,60000,WPFront User Role Editor +delete_others_awebookings,awebooking,6000,AweBooking – Hotel Booking System +delete_others_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_others_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_others_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_others_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_others_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_others_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_others_blocks,gutenberg,500000,Gutenberg +delete_others_board_committees,nonprofit-board-management,400,Nonprofit Board Management +delete_others_board_events,nonprofit-board-management,400,Nonprofit Board Management +delete_others_book-reviews,book-review-library,700,Book Review Library +delete_others_books,novelist,800,Novelist +delete_others_bps_forms,bp-profile-search,10000,BP Profile Search +delete_others_calp_events,calpress-event-calendar,5000,CalPress Calendar +delete_others_campaigns,charitable,10000,Charitable – Donation Plugin +delete_others_campaigns,leyka,1000,Leyka +delete_others_car_listings,wp-car-manager,3000,WP Car Manager +delete_others_cctor_coupons,coupon-creator,10000,Coupon Creator +delete_others_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +delete_others_classified_listings,classifieds-wp,800,Classifieds WP +delete_others_clients,upstream,1000,WordPress Project Management by UpStream +delete_others_courses,lifterlms,8000,LifterLMS +delete_others_ctas,cta,10000,WordPress Calls to Action +delete_others_custom_csss,custom-css-js,100000,Simple Custom CSS and JS +delete_others_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +delete_others_documents,wp-document-revisions,4000,WP Document Revisions +delete_others_donations,charitable,10000,Charitable – Donation Plugin +delete_others_donations,leyka,1000,Leyka +delete_others_dsn_notes,admin-dashboard-site-notes,3000,Dashboard Site Notes +delete_others_edr_courses,educator,1000,Educator 2 +delete_others_edr_lessons,educator,1000,Educator 2 +delete_others_edr_memberships,educator,1000,Educator 2 +delete_others_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +delete_others_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +delete_others_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_others_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_others_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +delete_others_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_others_emd_quotes,request-a-quote,1000,Request a Quote +delete_others_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_others_epa_albums,easy-photo-album,5000,Easy Photo Album +delete_others_event_listings,wp-event-manager,1000,WP Event Manager +delete_others_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +delete_others_events,event-organiser,40000,Event Organiser +delete_others_events,events-maker,4000,Events Maker by dFactory +delete_others_events,events-manager,100000,Events Manager +delete_others_events,quick-event-manager,5000,Quick Event Manager +delete_others_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +delete_others_fa_items,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +delete_others_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +delete_others_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +delete_others_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +delete_others_fep_announcements,front-end-pm,8000,Front End PM +delete_others_fep_messages,front-end-pm,8000,Front End PM +delete_others_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +delete_others_food_groups,restaurantpress,3000,RestaurantPress +delete_others_food_menus,restaurantpress,3000,RestaurantPress +delete_others_forms,pronamic-ideal,6000,Pronamic Pay +delete_others_forums,bbpress,300000,bbPress +delete_others_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_others_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +delete_others_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +delete_others_glossaries,glossary-by-codeat,1000,Glossary +delete_others_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +delete_others_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +delete_others_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_others_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_others_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +delete_others_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +delete_others_ib_edu_memberships,ibeducator,1000,Educator +delete_others_ib_educator_courses,ibeducator,1000,Educator +delete_others_ib_educator_lessons,ibeducator,1000,Educator +delete_others_ims_gallerys,image-store,900,Image Store +delete_others_inbound-forms,cta,10000,WordPress Calls to Action +delete_others_inbound-forms,landing-pages,10000,WordPress Landing Pages +delete_others_inbound-forms,leads,7000,WordPress Leads +delete_others_insertcodes,insert-code,300,Insert Code +delete_others_invoices,essential-real-estate,3000,Essential Real Estate +delete_others_job_listings,wp-job-manager,100000,WP Job Manager +delete_others_jscp_matchs,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_others_jscp_players,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_others_jscp_teams,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_others_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +delete_others_landing_pages,landing-pages,10000,WordPress Landing Pages +delete_others_leads,cta,10000,WordPress Calls to Action +delete_others_leads,landing-pages,10000,WordPress Landing Pages +delete_others_leads,leads,7000,WordPress Leads +delete_others_legalpack_pages,legalpack,400,Legalpack +delete_others_lessons,lifterlms,8000,LifterLMS +delete_others_listings,auto-listings,400,Auto Listings +delete_others_listings,wp-real-estate,400,WP Real Estate +delete_others_listings,wpcasa,2000,WPCasa +delete_others_locations,events-manager,100000,Events Manager +delete_others_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_others_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_others_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_others_mbdb_book,mooberry-book-manager,1000,Mooberry Book Manager +delete_others_mbdb_book_grid,mooberry-book-manager,1000,Mooberry Book Manager +delete_others_mbdb_book_grids,mooberry-book-manager,1000,Mooberry Book Manager +delete_others_mbdb_books,mooberry-book-manager,1000,Mooberry Book Manager +delete_others_meals,restaurant-manager,800,Restaurant Manager +delete_others_memberships,lifterlms,8000,LifterLMS +delete_others_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_others_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_others_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +delete_others_nemus-sliders,nemus-slider,2000,Nemus Slider +delete_others_news,news-manager,3000,News Manager +delete_others_newsletters,alo-easymail,10000,ALO EasyMail Newsletter +delete_others_opalestate_agentss,opal-estate,1000,Opal Estate +delete_others_opalestate_propertiess,opal-estate,1000,Opal Estate +delete_others_packages,essential-real-estate,3000,Essential Real Estate +delete_others_payments,pronamic-ideal,6000,Pronamic Pay +delete_others_players,team-rosters,800,Team Rosters +delete_others_playlists,radio-station,1000,Radio Station +delete_others_plugin_filters,plugin-organizer,10000,Plugin Organizer +delete_others_plugin_groups,plugin-organizer,10000,Plugin Organizer +delete_others_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +delete_others_portfolios,flash-toolkit,30000,Flash Toolkit +delete_others_portfolios,suffice-toolkit,5000,Suffice Toolkit +delete_others_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +delete_others_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +delete_others_products,design-approval-system,500,Design Approval System +delete_others_products,easy-digital-downloads,60000,Easy Digital Downloads +delete_others_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +delete_others_products,gnucommerce,1000,GNUCommerce +delete_others_products,jigoshop,4000,Jigoshop +delete_others_products,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_others_products,post-type-x,1000,Product Catalog X +delete_others_products,products,300,WP Products +delete_others_products,webmaster-user-role,8000,Webmaster User Role +delete_others_products,woocommerce,4000000,WooCommerce +delete_others_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_others_profile_cct,profile-custom-content-type,200,Profile CCT +delete_others_projects,upstream,1000,WordPress Project Management by UpStream +delete_others_propertys,essential-real-estate,3000,Essential Real Estate +delete_others_psp_projects,project-panorama-lite,1000,Project Panorama +delete_others_questions,lifterlms,8000,LifterLMS +delete_others_quizzes,lifterlms,8000,LifterLMS +delete_others_quotes,mg-quotes,300,mg Quotes +delete_others_recurring_events,events-manager,100000,Events Manager +delete_others_redirects,wp-redirects,700,WP Redirects +delete_others_rem_properties,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +delete_others_replies,bbpress,300000,bbPress +delete_others_reservations,restaurant-manager,800,Restaurant Manager +delete_others_resume_positions,wp-resume,700,WP Resume +delete_others_room_reservations,wp-hotelier,1000,Easy WP Hotelier +delete_others_room_types,awebooking,6000,AweBooking – Hotel Booking System +delete_others_rooms,wp-hotelier,1000,Easy WP Hotelier +delete_others_rsvpmakers,rsvpmaker,1000,RSVPMaker +delete_others_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_others_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +delete_others_shop_coupons,jigoshop,4000,Jigoshop +delete_others_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_others_shop_coupons,webmaster-user-role,8000,Webmaster User Role +delete_others_shop_coupons,woocommerce,4000000,WooCommerce +delete_others_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +delete_others_shop_emails,jigoshop,4000,Jigoshop +delete_others_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_others_shop_orders,jigoshop,4000,Jigoshop +delete_others_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_others_shop_orders,webmaster-user-role,8000,Webmaster User Role +delete_others_shop_orders,woocommerce,4000000,WooCommerce +delete_others_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +delete_others_shop_webhooks,woocommerce,4000000,WooCommerce +delete_others_shows,radio-station,1000,Radio Station +delete_others_sln_attendants,salon-booking-system,5000,Salon booking system +delete_others_sln_bookings,salon-booking-system,5000,Salon booking system +delete_others_sln_services,salon-booking-system,5000,Salon booking system +delete_others_snippets,wp-snippets,1000,WP Snippets +delete_others_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +delete_others_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +delete_others_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +delete_others_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +delete_others_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +delete_others_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +delete_others_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +delete_others_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +delete_others_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_others_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +delete_others_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_others_stores,wp-store-locator,50000,WP Store Locator +delete_others_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_others_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_others_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_others_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +delete_others_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_others_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_others_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_others_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_others_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_others_tm-propertys,cherry-real-estate,600,Cherry Real Estate +delete_others_topics,bbpress,300000,bbPress +delete_others_total_slider_slides,total-slider,500,Total Slider +delete_others_trans_logs,essential-real-estate,3000,Essential Real Estate +delete_others_translations,simple-punctual-translation,200,Simple Punctual Translation +delete_others_tribe_events,the-events-calendar,700000,The Events Calendar +delete_others_tribe_organizers,the-events-calendar,700000,The Events Calendar +delete_others_tribe_venues,the-events-calendar,700000,The Events Calendar +delete_others_user_packages,essential-real-estate,3000,Essential Real Estate +delete_others_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +delete_others_vacancies,job-board,300,Job Board by BestWebSoft +delete_others_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_others_wctrl_contents,widgets-control,1000,Widgets Control +delete_others_wd_ads_adverts,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +delete_others_wd_ads_schedules,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +delete_others_wiki_pages,wordpress-wiki,400,WordPress Wiki +delete_others_wordlift_entities,wordlift,400,WordLift – AI powered SEO +delete_others_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +delete_others_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_others_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_others_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_others_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_others_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_others_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +delete_others_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +delete_others_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_others_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_others_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_others_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_others_wpp_properties,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +delete_others_wppizzas,wppizza,2000,WPPizza +delete_others_wprm_reservations,wp-restaurant-manager,700,WP Restaurant Manager +delete_others_wpsdealss,deals-engine,200,Social Deals Engine +delete_others_wpsdealssaless,deals-engine,200,Social Deals Engine +delete_others_wpse_profiles,wp-smart-editor,900,WP Smart Editor +delete_others_ycd_countdowns,countdown-builder,1000,Countdown +delete_own_touchpoints,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +delete_own_ukuupeoples,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +delete_own_yop_polls,yop-poll,20000,YOP Poll +delete_own_yop_polls_templates,yop-poll,20000,YOP Poll +delete_package,essential-real-estate,3000,Essential Real Estate +delete_package_terms,essential-real-estate,3000,Essential Real Estate +delete_packages,essential-real-estate,3000,Essential Real Estate +delete_page_in_section,bu-section-editing,300,BU Section Editing +delete_payment,pronamic-ideal,6000,Pronamic Pay +delete_payments,pronamic-ideal,6000,Pronamic Pay +delete_person_area,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +delete_person_location,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +delete_person_rareas,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +delete_person_title,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +delete_player,team-rosters,800,Team Rosters +delete_players,team-rosters,800,Team Rosters +delete_playlists,radio-station,1000,Radio Station +delete_plugin_filter,plugin-organizer,10000,Plugin Organizer +delete_plugin_filters,plugin-organizer,10000,Plugin Organizer +delete_plugin_group,plugin-organizer,10000,Plugin Organizer +delete_plugin_groups,plugin-organizer,10000,Plugin Organizer +delete_portfolio,flash-toolkit,30000,Flash Toolkit +delete_portfolio,suffice-toolkit,5000,Suffice Toolkit +delete_portfolio,visual-portfolio,7000,Visual Portfolio +delete_portfolio_categories,custom-content-portfolio,1000,Custom Content Portfolio +delete_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +delete_portfolio_tags,custom-content-portfolio,1000,Custom Content Portfolio +delete_portfolio_terms,flash-toolkit,30000,Flash Toolkit +delete_portfolio_terms,suffice-toolkit,5000,Suffice Toolkit +delete_portfolios,flash-toolkit,30000,Flash Toolkit +delete_portfolios,suffice-toolkit,5000,Suffice Toolkit +delete_portfolios,visual-portfolio,7000,Visual Portfolio +delete_post_ims_gallery,image-store,900,Image Store +delete_post_in_section,bu-section-editing,300,BU Section Editing +delete_posts_ims_gallery,image-store,900,Image Store +delete_pricing_rate,awebooking,6000,AweBooking – Hotel Booking System +delete_pricing_rate_terms,awebooking,6000,AweBooking – Hotel Booking System +delete_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +delete_private_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_private_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_private_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_private_aec_events,another-events-calendar,800,Another Events Calendar +delete_private_aec_organizers,another-events-calendar,800,Another Events Calendar +delete_private_aec_venues,another-events-calendar,800,Another Events Calendar +delete_private_affiliate_keywords,affiliate,700,Affiliate +delete_private_agents,essential-real-estate,3000,Essential Real Estate +delete_private_aggregator-records,the-events-calendar,700000,The Events Calendar +delete_private_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +delete_private_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +delete_private_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +delete_private_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +delete_private_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +delete_private_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +delete_private_anbs,alert-notice-boxes,1000,Alert Notice Boxes +delete_private_archivs,archive,700,Archive +delete_private_articles,issuem,1000,IssueM +delete_private_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +delete_private_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +delete_private_awebookings,awebooking,6000,AweBooking – Hotel Booking System +delete_private_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_private_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_private_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_private_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_private_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_private_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_private_blocks,gutenberg,500000,Gutenberg +delete_private_board_committees,nonprofit-board-management,400,Nonprofit Board Management +delete_private_board_events,nonprofit-board-management,400,Nonprofit Board Management +delete_private_books,novelist,800,Novelist +delete_private_calp_events,calpress-event-calendar,5000,CalPress Calendar +delete_private_campaigns,charitable,10000,Charitable – Donation Plugin +delete_private_campaigns,leyka,1000,Leyka +delete_private_car_listings,wp-car-manager,3000,WP Car Manager +delete_private_cctor_coupons,coupon-creator,10000,Coupon Creator +delete_private_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +delete_private_classified_listings,classifieds-wp,800,Classifieds WP +delete_private_clients,upstream,1000,WordPress Project Management by UpStream +delete_private_courses,lifterlms,8000,LifterLMS +delete_private_ctas,cta,10000,WordPress Calls to Action +delete_private_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +delete_private_documents,wp-document-revisions,4000,WP Document Revisions +delete_private_donations,charitable,10000,Charitable – Donation Plugin +delete_private_donations,leyka,1000,Leyka +delete_private_edr_courses,educator,1000,Educator 2 +delete_private_edr_lessons,educator,1000,Educator 2 +delete_private_edr_memberships,educator,1000,Educator 2 +delete_private_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +delete_private_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +delete_private_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_private_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_private_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +delete_private_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_private_emd_quotes,request-a-quote,1000,Request a Quote +delete_private_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_private_epa_albums,easy-photo-album,5000,Easy Photo Album +delete_private_event_listings,wp-event-manager,1000,WP Event Manager +delete_private_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +delete_private_events,quick-event-manager,5000,Quick Event Manager +delete_private_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +delete_private_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +delete_private_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +delete_private_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +delete_private_fep_announcements,front-end-pm,8000,Front End PM +delete_private_fep_messages,front-end-pm,8000,Front End PM +delete_private_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +delete_private_food_groups,restaurantpress,3000,RestaurantPress +delete_private_food_menus,restaurantpress,3000,RestaurantPress +delete_private_forms,pronamic-ideal,6000,Pronamic Pay +delete_private_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +delete_private_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +delete_private_glossaries,glossary-by-codeat,1000,Glossary +delete_private_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +delete_private_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +delete_private_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_private_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_private_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +delete_private_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +delete_private_ib_edu_memberships,ibeducator,1000,Educator +delete_private_ib_educator_courses,ibeducator,1000,Educator +delete_private_ib_educator_lessons,ibeducator,1000,Educator +delete_private_ims_gallery,image-store,900,Image Store +delete_private_inbound-forms,cta,10000,WordPress Calls to Action +delete_private_inbound-forms,landing-pages,10000,WordPress Landing Pages +delete_private_inbound-forms,leads,7000,WordPress Leads +delete_private_insertcodes,insert-code,300,Insert Code +delete_private_invoices,essential-real-estate,3000,Essential Real Estate +delete_private_job_listings,wp-job-manager,100000,WP Job Manager +delete_private_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +delete_private_landing_pages,landing-pages,10000,WordPress Landing Pages +delete_private_leads,cta,10000,WordPress Calls to Action +delete_private_leads,landing-pages,10000,WordPress Landing Pages +delete_private_leads,leads,7000,WordPress Leads +delete_private_legalpack_pages,legalpack,400,Legalpack +delete_private_lessons,lifterlms,8000,LifterLMS +delete_private_listings,auto-listings,400,Auto Listings +delete_private_listings,wp-real-estate,400,WP Real Estate +delete_private_listings,wpcasa,2000,WPCasa +delete_private_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_private_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_private_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_private_meals,restaurant-manager,800,Restaurant Manager +delete_private_memberships,lifterlms,8000,LifterLMS +delete_private_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_private_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_private_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +delete_private_nemus-sliders,nemus-slider,2000,Nemus Slider +delete_private_opalestate_agentss,opal-estate,1000,Opal Estate +delete_private_opalestate_propertiess,opal-estate,1000,Opal Estate +delete_private_packages,essential-real-estate,3000,Essential Real Estate +delete_private_payments,pronamic-ideal,6000,Pronamic Pay +delete_private_playlists,radio-station,1000,Radio Station +delete_private_plugin_filters,plugin-organizer,10000,Plugin Organizer +delete_private_plugin_groups,plugin-organizer,10000,Plugin Organizer +delete_private_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +delete_private_portfolios,flash-toolkit,30000,Flash Toolkit +delete_private_portfolios,suffice-toolkit,5000,Suffice Toolkit +delete_private_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +delete_private_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +delete_private_products,design-approval-system,500,Design Approval System +delete_private_products,easy-digital-downloads,60000,Easy Digital Downloads +delete_private_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +delete_private_products,gnucommerce,1000,GNUCommerce +delete_private_products,jigoshop,4000,Jigoshop +delete_private_products,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_private_products,post-type-x,1000,Product Catalog X +delete_private_products,products,300,WP Products +delete_private_products,webmaster-user-role,8000,Webmaster User Role +delete_private_products,woocommerce,4000000,WooCommerce +delete_private_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_private_projects,upstream,1000,WordPress Project Management by UpStream +delete_private_propertys,essential-real-estate,3000,Essential Real Estate +delete_private_psp_projects,project-panorama-lite,1000,Project Panorama +delete_private_questions,lifterlms,8000,LifterLMS +delete_private_quizzes,lifterlms,8000,LifterLMS +delete_private_quotes,mg-quotes,300,mg Quotes +delete_private_redirects,wp-redirects,700,WP Redirects +delete_private_rem_properties,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +delete_private_reservations,restaurant-manager,800,Restaurant Manager +delete_private_resume_positions,wp-resume,700,WP Resume +delete_private_room_reservations,wp-hotelier,1000,Easy WP Hotelier +delete_private_room_types,awebooking,6000,AweBooking – Hotel Booking System +delete_private_rooms,wp-hotelier,1000,Easy WP Hotelier +delete_private_rsvpmakers,rsvpmaker,1000,RSVPMaker +delete_private_sgpb_popup,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +delete_private_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +delete_private_shop_coupons,jigoshop,4000,Jigoshop +delete_private_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_private_shop_coupons,webmaster-user-role,8000,Webmaster User Role +delete_private_shop_coupons,woocommerce,4000000,WooCommerce +delete_private_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +delete_private_shop_emails,jigoshop,4000,Jigoshop +delete_private_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_private_shop_orders,jigoshop,4000,Jigoshop +delete_private_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_private_shop_orders,webmaster-user-role,8000,Webmaster User Role +delete_private_shop_orders,woocommerce,4000000,WooCommerce +delete_private_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +delete_private_shop_webhooks,woocommerce,4000000,WooCommerce +delete_private_shows,radio-station,1000,Radio Station +delete_private_sln_attendants,salon-booking-system,5000,Salon booking system +delete_private_sln_bookings,salon-booking-system,5000,Salon booking system +delete_private_sln_services,salon-booking-system,5000,Salon booking system +delete_private_snippets,wp-snippets,1000,WP Snippets +delete_private_snitchs,snitch,1000,Snitch +delete_private_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +delete_private_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +delete_private_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +delete_private_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +delete_private_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +delete_private_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +delete_private_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +delete_private_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +delete_private_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +delete_private_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_private_stores,wp-store-locator,50000,WP Store Locator +delete_private_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_private_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_private_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_private_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +delete_private_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_private_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_private_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_private_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_private_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +delete_private_tm-propertys,cherry-real-estate,600,Cherry Real Estate +delete_private_total_slider_slides,total-slider,500,Total Slider +delete_private_trans_logs,essential-real-estate,3000,Essential Real Estate +delete_private_translations,simple-punctual-translation,200,Simple Punctual Translation +delete_private_tribe_events,the-events-calendar,700000,The Events Calendar +delete_private_tribe_organizers,the-events-calendar,700000,The Events Calendar +delete_private_tribe_venues,the-events-calendar,700000,The Events Calendar +delete_private_user_packages,essential-real-estate,3000,Essential Real Estate +delete_private_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +delete_private_vacancies,job-board,300,Job Board by BestWebSoft +delete_private_wctrl_contents,widgets-control,1000,Widgets Control +delete_private_wordlift_entities,wordlift,400,WordLift – AI powered SEO +delete_private_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +delete_private_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_private_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_private_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_private_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_private_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_private_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +delete_private_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +delete_private_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_private_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_private_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_private_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_private_wpp_properties,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +delete_private_wprm_reservations,wp-restaurant-manager,700,WP Restaurant Manager +delete_private_wpsdealss,deals-engine,200,Social Deals Engine +delete_private_wpsdealssaless,deals-engine,200,Social Deals Engine +delete_private_ycd_countdown,countdown-builder,1000,Countdown +delete_private_ycd_countdowns,countdown-builder,1000,Countdown +delete_product,dc-woocommerce-multi-vendor,10000,WC Marketplace +delete_product,design-approval-system,500,Design Approval System +delete_product,easy-digital-downloads,60000,Easy Digital Downloads +delete_product,gnucommerce,1000,GNUCommerce +delete_product,jigoshop,4000,Jigoshop +delete_product,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_product,webmaster-user-role,8000,Webmaster User Role +delete_product,woocommerce,4000000,WooCommerce +delete_product,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_product,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +delete_product_categories,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +delete_product_categories,post-type-x,1000,Product Catalog X +delete_product_set,datafeedr-product-sets,1000,Datafeedr Product Sets +delete_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +delete_product_terms,easy-digital-downloads,60000,Easy Digital Downloads +delete_product_terms,jigoshop,4000,Jigoshop +delete_product_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_product_terms,webmaster-user-role,8000,Webmaster User Role +delete_product_terms,woocommerce,4000000,WooCommerce +delete_products,dc-woocommerce-multi-vendor,10000,WC Marketplace +delete_products,design-approval-system,500,Design Approval System +delete_products,easy-digital-downloads,60000,Easy Digital Downloads +delete_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +delete_products,gnucommerce,1000,GNUCommerce +delete_products,jigoshop,4000,Jigoshop +delete_products,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_products,post-type-x,1000,Product Catalog X +delete_products,products,300,WP Products +delete_products,webmaster-user-role,8000,Webmaster User Role +delete_products,woocommerce,4000000,WooCommerce +delete_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_products,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +delete_profile_cct,profile-custom-content-type,200,Profile CCT +delete_project,upstream,1000,WordPress Project Management by UpStream +delete_project_discussion,upstream,1000,WordPress Project Management by UpStream +delete_project_terms,upstream,1000,WordPress Project Management by UpStream +delete_projects,projectmanager,400,ProjectManager +delete_projects,upstream,1000,WordPress Project Management by UpStream +delete_property,essential-real-estate,3000,Essential Real Estate +delete_property_terms,essential-real-estate,3000,Essential Real Estate +delete_propertys,essential-real-estate,3000,Essential Real Estate +delete_psp_projects,project-panorama-lite,1000,Project Panorama +delete_published_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_published_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_published_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +delete_published_ads,apply-online,5000,Apply Online +delete_published_aec_events,another-events-calendar,800,Another Events Calendar +delete_published_aec_organizers,another-events-calendar,800,Another Events Calendar +delete_published_aec_venues,another-events-calendar,800,Another Events Calendar +delete_published_affiliate_keywords,affiliate,700,Affiliate +delete_published_agents,essential-real-estate,3000,Essential Real Estate +delete_published_aggregator-records,the-events-calendar,700000,The Events Calendar +delete_published_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +delete_published_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +delete_published_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +delete_published_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +delete_published_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +delete_published_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +delete_published_anbs,alert-notice-boxes,1000,Alert Notice Boxes +delete_published_applications,apply-online,5000,Apply Online +delete_published_archivs,archive,700,Archive +delete_published_articles,issuem,1000,IssueM +delete_published_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +delete_published_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +delete_published_awebookings,awebooking,6000,AweBooking – Hotel Booking System +delete_published_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_published_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_published_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_published_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_published_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_published_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +delete_published_blocks,gutenberg,500000,Gutenberg +delete_published_board_committees,nonprofit-board-management,400,Nonprofit Board Management +delete_published_board_events,nonprofit-board-management,400,Nonprofit Board Management +delete_published_book-reviews,book-review-library,700,Book Review Library +delete_published_books,novelist,800,Novelist +delete_published_bps_forms,bp-profile-search,10000,BP Profile Search +delete_published_calp_events,calpress-event-calendar,5000,CalPress Calendar +delete_published_campaigns,charitable,10000,Charitable – Donation Plugin +delete_published_campaigns,leyka,1000,Leyka +delete_published_car_listings,wp-car-manager,3000,WP Car Manager +delete_published_cctor_coupons,coupon-creator,10000,Coupon Creator +delete_published_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +delete_published_classified_listings,classifieds-wp,800,Classifieds WP +delete_published_clients,upstream,1000,WordPress Project Management by UpStream +delete_published_courses,lifterlms,8000,LifterLMS +delete_published_ctas,cta,10000,WordPress Calls to Action +delete_published_custom_csss,custom-css-js,100000,Simple Custom CSS and JS +delete_published_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +delete_published_documents,wp-document-revisions,4000,WP Document Revisions +delete_published_donations,charitable,10000,Charitable – Donation Plugin +delete_published_donations,leyka,1000,Leyka +delete_published_edr_courses,educator,1000,Educator 2 +delete_published_edr_lessons,educator,1000,Educator 2 +delete_published_edr_memberships,educator,1000,Educator 2 +delete_published_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +delete_published_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +delete_published_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_published_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_published_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +delete_published_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +delete_published_emd_quotes,request-a-quote,1000,Request a Quote +delete_published_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_published_epa_albums,easy-photo-album,5000,Easy Photo Album +delete_published_event_listings,wp-event-manager,1000,WP Event Manager +delete_published_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +delete_published_events,events-maker,4000,Events Maker by dFactory +delete_published_events,quick-event-manager,5000,Quick Event Manager +delete_published_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +delete_published_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +delete_published_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +delete_published_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +delete_published_fep_announcements,front-end-pm,8000,Front End PM +delete_published_fep_messages,front-end-pm,8000,Front End PM +delete_published_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +delete_published_food_groups,restaurantpress,3000,RestaurantPress +delete_published_food_menus,restaurantpress,3000,RestaurantPress +delete_published_forms,pronamic-ideal,6000,Pronamic Pay +delete_published_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_published_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +delete_published_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +delete_published_glossaries,glossary-by-codeat,1000,Glossary +delete_published_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +delete_published_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +delete_published_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_published_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +delete_published_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +delete_published_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +delete_published_ib_edu_memberships,ibeducator,1000,Educator +delete_published_ib_educator_courses,ibeducator,1000,Educator +delete_published_ib_educator_lessons,ibeducator,1000,Educator +delete_published_ims_gallery,image-store,900,Image Store +delete_published_inbound-forms,cta,10000,WordPress Calls to Action +delete_published_inbound-forms,landing-pages,10000,WordPress Landing Pages +delete_published_inbound-forms,leads,7000,WordPress Leads +delete_published_insertcodes,insert-code,300,Insert Code +delete_published_invoices,essential-real-estate,3000,Essential Real Estate +delete_published_job_listings,wp-job-manager,100000,WP Job Manager +delete_published_jscp_matchs,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_published_jscp_players,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_published_jscp_teams,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +delete_published_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +delete_published_landing_pages,landing-pages,10000,WordPress Landing Pages +delete_published_leads,cta,10000,WordPress Calls to Action +delete_published_leads,landing-pages,10000,WordPress Landing Pages +delete_published_leads,leads,7000,WordPress Leads +delete_published_legalpack_pages,legalpack,400,Legalpack +delete_published_lessons,lifterlms,8000,LifterLMS +delete_published_listings,auto-listings,400,Auto Listings +delete_published_listings,wp-real-estate,400,WP Real Estate +delete_published_listings,wpcasa,2000,WPCasa +delete_published_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_published_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_published_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +delete_published_mbdb_book,mooberry-book-manager,1000,Mooberry Book Manager +delete_published_mbdb_book_grid,mooberry-book-manager,1000,Mooberry Book Manager +delete_published_mbdb_book_grids,mooberry-book-manager,1000,Mooberry Book Manager +delete_published_mbdb_books,mooberry-book-manager,1000,Mooberry Book Manager +delete_published_meals,restaurant-manager,800,Restaurant Manager +delete_published_memberships,lifterlms,8000,LifterLMS +delete_published_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_published_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +delete_published_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +delete_published_nemus-sliders,nemus-slider,2000,Nemus Slider +delete_published_news,news-manager,3000,News Manager +delete_published_opalestate_agentss,opal-estate,1000,Opal Estate +delete_published_opalestate_propertiess,opal-estate,1000,Opal Estate +delete_published_packages,essential-real-estate,3000,Essential Real Estate +delete_published_payments,pronamic-ideal,6000,Pronamic Pay +delete_published_players,team-rosters,800,Team Rosters +delete_published_playlists,radio-station,1000,Radio Station +delete_published_plugin_filters,plugin-organizer,10000,Plugin Organizer +delete_published_plugin_groups,plugin-organizer,10000,Plugin Organizer +delete_published_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +delete_published_portfolios,flash-toolkit,30000,Flash Toolkit +delete_published_portfolios,suffice-toolkit,5000,Suffice Toolkit +delete_published_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +delete_published_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +delete_published_products,design-approval-system,500,Design Approval System +delete_published_products,easy-digital-downloads,60000,Easy Digital Downloads +delete_published_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +delete_published_products,gnucommerce,1000,GNUCommerce +delete_published_products,jigoshop,4000,Jigoshop +delete_published_products,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_published_products,post-type-x,1000,Product Catalog X +delete_published_products,products,300,WP Products +delete_published_products,webmaster-user-role,8000,Webmaster User Role +delete_published_products,woocommerce,4000000,WooCommerce +delete_published_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_published_products,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +delete_published_projects,upstream,1000,WordPress Project Management by UpStream +delete_published_propertys,essential-real-estate,3000,Essential Real Estate +delete_published_psp_projects,project-panorama-lite,1000,Project Panorama +delete_published_questions,lifterlms,8000,LifterLMS +delete_published_quizzes,lifterlms,8000,LifterLMS +delete_published_quotes,mg-quotes,300,mg Quotes +delete_published_redirects,wp-redirects,700,WP Redirects +delete_published_rem_properties,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +delete_published_reservations,restaurant-manager,800,Restaurant Manager +delete_published_resume_positions,wp-resume,700,WP Resume +delete_published_room_reservations,wp-hotelier,1000,Easy WP Hotelier +delete_published_room_types,awebooking,6000,AweBooking – Hotel Booking System +delete_published_rooms,wp-hotelier,1000,Easy WP Hotelier +delete_published_rsvpmakers,rsvpmaker,1000,RSVPMaker +delete_published_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_published_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +delete_published_shop_coupons,jigoshop,4000,Jigoshop +delete_published_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_published_shop_coupons,webmaster-user-role,8000,Webmaster User Role +delete_published_shop_coupons,woocommerce,4000000,WooCommerce +delete_published_shop_coupons,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +delete_published_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +delete_published_shop_emails,jigoshop,4000,Jigoshop +delete_published_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_published_shop_orders,jigoshop,4000,Jigoshop +delete_published_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_published_shop_orders,webmaster-user-role,8000,Webmaster User Role +delete_published_shop_orders,woocommerce,4000000,WooCommerce +delete_published_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +delete_published_shop_webhooks,woocommerce,4000000,WooCommerce +delete_published_shows,radio-station,1000,Radio Station +delete_published_sln_attendants,salon-booking-system,5000,Salon booking system +delete_published_sln_bookings,salon-booking-system,5000,Salon booking system +delete_published_sln_services,salon-booking-system,5000,Salon booking system +delete_published_snippets,wp-snippets,1000,WP Snippets +delete_published_snitchs,snitch,1000,Snitch +delete_published_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +delete_published_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +delete_published_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +delete_published_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +delete_published_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +delete_published_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +delete_published_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +delete_published_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +delete_published_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_published_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +delete_published_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_published_stores,wp-store-locator,50000,WP Store Locator +delete_published_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_published_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_published_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_published_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +delete_published_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_published_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_published_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_published_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_published_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_published_tm-propertys,cherry-real-estate,600,Cherry Real Estate +delete_published_total_slider_slides,total-slider,500,Total Slider +delete_published_trans_logs,essential-real-estate,3000,Essential Real Estate +delete_published_translations,simple-punctual-translation,200,Simple Punctual Translation +delete_published_tribe_events,the-events-calendar,700000,The Events Calendar +delete_published_tribe_organizers,the-events-calendar,700000,The Events Calendar +delete_published_tribe_venues,the-events-calendar,700000,The Events Calendar +delete_published_user_packages,essential-real-estate,3000,Essential Real Estate +delete_published_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +delete_published_vacancies,job-board,300,Job Board by BestWebSoft +delete_published_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_published_wctrl_contents,widgets-control,1000,Widgets Control +delete_published_wordlift_entities,wordlift,400,WordLift – AI powered SEO +delete_published_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +delete_published_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_published_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_published_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_published_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_published_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_published_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +delete_published_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +delete_published_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_published_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_published_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_published_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_published_wppizzas,wppizza,2000,WPPizza +delete_published_wprm_reservations,wp-restaurant-manager,700,WP Restaurant Manager +delete_published_wpsdealss,deals-engine,200,Social Deals Engine +delete_published_wpsdealssaless,deals-engine,200,Social Deals Engine +delete_published_ycd_countdowns,countdown-builder,1000,Countdown +delete_question,lifterlms,8000,LifterLMS +delete_questions,lifterlms,8000,LifterLMS +delete_quiz,lifterlms,8000,LifterLMS +delete_quizzes,lifterlms,8000,LifterLMS +delete_quote_authors,mg-quotes,300,mg Quotes +delete_quote_categories,mg-quotes,300,mg Quotes +delete_quotes,mg-quotes,300,mg Quotes +delete_raq_services,request-a-quote,1000,Request a Quote +delete_recurring_events,events-manager,100000,Events Manager +delete_redirects,wp-redirects,700,WP Redirects +delete_replies,bbpress,300000,bbPress +delete_reply,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +delete_reply,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +delete_reservations,restaurant-manager,800,Restaurant Manager +delete_resume_organizations,wp-resume,700,WP Resume +delete_resume_positions,wp-resume,700,WP Resume +delete_resume_sections,wp-resume,700,WP Resume +delete_roles,members,100000,Members +delete_roles,wpfront-user-role-editor,60000,WPFront User Role Editor +delete_room,wp-hotelier,1000,Easy WP Hotelier +delete_room_reservation,wp-hotelier,1000,Easy WP Hotelier +delete_room_reservation_terms,wp-hotelier,1000,Easy WP Hotelier +delete_room_reservations,wp-hotelier,1000,Easy WP Hotelier +delete_room_terms,wp-hotelier,1000,Easy WP Hotelier +delete_room_type,awebooking,6000,AweBooking – Hotel Booking System +delete_room_type_terms,awebooking,6000,AweBooking – Hotel Booking System +delete_room_types,awebooking,6000,AweBooking – Hotel Booking System +delete_rooms,wp-hotelier,1000,Easy WP Hotelier +delete_rsvpemail,rsvpmaker,1000,RSVPMaker +delete_rsvpmakers,rsvpmaker,1000,RSVPMaker +delete_schedule,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +delete_shift,employee-scheduler,400,Shiftee Basic – Employee and Staff Scheduling +delete_shifts,employee-scheduler,400,Shiftee Basic – Employee and Staff Scheduling +delete_shop_coupon,jigoshop,4000,Jigoshop +delete_shop_coupon,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_coupon,webmaster-user-role,8000,Webmaster User Role +delete_shop_coupon,woocommerce,4000000,WooCommerce +delete_shop_coupon_terms,jigoshop,4000,Jigoshop +delete_shop_coupon_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_coupon_terms,webmaster-user-role,8000,Webmaster User Role +delete_shop_coupon_terms,woocommerce,4000000,WooCommerce +delete_shop_coupons,jigoshop,4000,Jigoshop +delete_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_coupons,webmaster-user-role,8000,Webmaster User Role +delete_shop_coupons,woocommerce,4000000,WooCommerce +delete_shop_coupons,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +delete_shop_discount,easy-digital-downloads,60000,Easy Digital Downloads +delete_shop_discount_terms,easy-digital-downloads,60000,Easy Digital Downloads +delete_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +delete_shop_email,jigoshop,4000,Jigoshop +delete_shop_email,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_email_terms,jigoshop,4000,Jigoshop +delete_shop_email_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_emails,jigoshop,4000,Jigoshop +delete_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_order,jigoshop,4000,Jigoshop +delete_shop_order,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_order,webmaster-user-role,8000,Webmaster User Role +delete_shop_order,woocommerce,4000000,WooCommerce +delete_shop_order_terms,jigoshop,4000,Jigoshop +delete_shop_order_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_order_terms,webmaster-user-role,8000,Webmaster User Role +delete_shop_order_terms,woocommerce,4000000,WooCommerce +delete_shop_orders,jigoshop,4000,Jigoshop +delete_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +delete_shop_orders,webmaster-user-role,8000,Webmaster User Role +delete_shop_orders,woocommerce,4000000,WooCommerce +delete_shop_payment,easy-digital-downloads,60000,Easy Digital Downloads +delete_shop_payment_terms,easy-digital-downloads,60000,Easy Digital Downloads +delete_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +delete_shop_webhook,woocommerce,4000000,WooCommerce +delete_shop_webhook_terms,woocommerce,4000000,WooCommerce +delete_shop_webhooks,woocommerce,4000000,WooCommerce +delete_shows,radio-station,1000,Radio Station +delete_sln_attendant,salon-booking-system,5000,Salon booking system +delete_sln_attendants,salon-booking-system,5000,Salon booking system +delete_sln_booking,salon-booking-system,5000,Salon booking system +delete_sln_bookings,salon-booking-system,5000,Salon booking system +delete_sln_service,salon-booking-system,5000,Salon booking system +delete_sln_services,salon-booking-system,5000,Salon booking system +delete_snippets,wp-snippets,1000,WP Snippets +delete_snitch,snitch,1000,Snitch +delete_snitchs,snitch,1000,Snitch +delete_sola_st_tickets,sola-support-tickets,400,Sola Support Tickets +delete_sp_calendar,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_calendar_terms,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_config,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_config_terms,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_event,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_event_terms,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_list,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_list_terms,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_player,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_player_terms,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_staff,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_staff_terms,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_table,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_table_terms,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_team,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_team_terms,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +delete_sport,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_sprout_invoices,sprout-invoices,2000,Client Invoicing by Sprout Invoices – Easy Estimates and Invoices for WordPress +delete_stm_lms_post,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +delete_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +delete_store,wp-store-locator,50000,WP Store Locator +delete_store_order,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +delete_stores,wp-store-locator,50000,WP Store Locator +delete_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_sunshine_gallery,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_sunshine_order,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_sunshine_product,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +delete_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +delete_tc_event,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_tc_order,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_tc_ticket,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_tc_tickets_instance,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +delete_team,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +delete_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +delete_ticket_priority,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_ticket_status,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_ticket_topic,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +delete_tm-propertys,cherry-real-estate,600,Cherry Real Estate +delete_topic_tags,bbpress,300000,bbPress +delete_topics,bbpress,300000,bbPress +delete_total_slider_slides,total-slider,500,Total Slider +delete_trans_log,essential-real-estate,3000,Essential Real Estate +delete_trans_log_terms,essential-real-estate,3000,Essential Real Estate +delete_trans_logs,essential-real-estate,3000,Essential Real Estate +delete_translation,simple-punctual-translation,200,Simple Punctual Translation +delete_translations,simple-punctual-translation,200,Simple Punctual Translation +delete_tribe_events,the-events-calendar,700000,The Events Calendar +delete_tribe_organizers,the-events-calendar,700000,The Events Calendar +delete_tribe_venues,the-events-calendar,700000,The Events Calendar +delete_un_feedback,usernoise,7000,Usernoise modal feedback / contact form +delete_user_package,essential-real-estate,3000,Essential Real Estate +delete_user_package_terms,essential-real-estate,3000,Essential Real Estate +delete_user_packages,essential-real-estate,3000,Essential Real Estate +delete_user_registration,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +delete_user_registration_terms,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +delete_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +delete_users_higher_level,wpfront-user-role-editor,60000,WPFront User Role Editor +delete_vacancies,job-board,300,Job Board by BestWebSoft +delete_vacancy,job-board,300,Job Board by BestWebSoft +delete_venue,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +delete_wbcr-snippets,insert-php,100000,PHP code snippets (Insert PHP) +delete_wctrl_contents,widgets-control,1000,Widgets Control +delete_wd_ads_advert,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +delete_wd_ads_adverts,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +delete_wd_ads_groups,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +delete_wd_ads_schedule,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +delete_wd_ads_schedules,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +delete_wiki_page,wordpress-wiki,400,WordPress Wiki +delete_wordlift_entities,wordlift,400,WordLift – AI powered SEO +delete_wordlift_entity,wordlift,400,WordLift – AI powered SEO +delete_wordpoints_extensions,wordpoints,700,WordPoints +delete_wordpoints_modules,wordpoints,700,WordPoints +delete_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +delete_wpcm_club,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_club_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_match,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_match_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_player,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_player_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_sponsor,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_sponsor_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_staff,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_staff_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +delete_wpdiscuz_form,wpdiscuz,40000,Comments – wpDiscuz +delete_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +delete_wpfc_sermon,sermon-manager-for-wordpress,9000,Sermon Manager +delete_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +delete_wpi_discount,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_wpi_invoice,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_wpi_item,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_wpi_quote,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +delete_wplc_quick_response,wp-live-chat-support,60000,WP Live Chat Support +delete_wpp_properties,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +delete_wpp_property,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +delete_wppizza,wppizza,2000,WPPizza +delete_wppizzas,wppizza,2000,WPPizza +delete_wpsdeals,deals-engine,200,Social Deals Engine +delete_wpsdeals_terms,deals-engine,200,Social Deals Engine +delete_wpsdealss,deals-engine,200,Social Deals Engine +delete_wpsdealssales,deals-engine,200,Social Deals Engine +delete_wpsdealssales_terms,deals-engine,200,Social Deals Engine +delete_wpsdealssaless,deals-engine,200,Social Deals Engine +delete_wpse_profiles,wp-smart-editor,900,WP Smart Editor +delete_wswebinar,wp-webinarsystem,2000,WP WebinarSystem +delete_wswebinars,wp-webinarsystem,2000,WP WebinarSystem +delete_ycd_countdowns,countdown-builder,1000,Countdown +delete_yop_polls,yop-poll,20000,YOP Poll +delete_yop_polls_logs,yop-poll,20000,YOP Poll +delete_yop_polls_templates,yop-poll,20000,YOP Poll +design_wpas,wp-app-studio,300,Professional WordPress Plugin Development – WP App Studio +developer_updates,developer-mode,1000,Developer Mode +diagnosis_read,diagnosis,300,Diagnosis +disable_h5p_security,h5p,10000,Interactive Content – H5P +dlm_manage_logs,download-monitor,100000,Download Monitor +dlm_view_reports,download-monitor,100000,Download Monitor +do_not_allow,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +dsn_notes,admin-dashboard-site-notes,3000,Dashboard Site Notes +duplicate_everest_form,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +duplicate_masterslider,master-slider,100000,Master Slider – Responsive Touch Slider +dwqa_can_delete_answer,dw-question-answer,10000,DW Question & Answer +dwqa_can_delete_comment,dw-question-answer,10000,DW Question & Answer +dwqa_can_delete_question,dw-question-answer,10000,DW Question & Answer +dwqa_can_edit_answer,dw-question-answer,10000,DW Question & Answer +dwqa_can_edit_comment,dw-question-answer,10000,DW Question & Answer +dwqa_can_edit_question,dw-question-answer,10000,DW Question & Answer +dwqa_can_post_answer,dw-question-answer,10000,DW Question & Answer +dwqa_can_post_comment,dw-question-answer,10000,DW Question & Answer +dwqa_can_post_question,dw-question-answer,10000,DW Question & Answer +dwqa_can_read_answer,dw-question-answer,10000,DW Question & Answer +dwqa_can_read_comment,dw-question-answer,10000,DW Question & Answer +dwqa_can_read_question,dw-question-answer,10000,DW Question & Answer +easingslider_delete_sliders,easing-slider,60000,Easing Slider +easingslider_duplicate_sliders,easing-slider,60000,Easing Slider +easingslider_edit_sliders,easing-slider,60000,Easing Slider +easingslider_manage_addons,easing-slider,60000,Easing Slider +easingslider_manage_settings,easing-slider,60000,Easing Slider +easingslider_publish_sliders,easing-slider,60000,Easing Slider +edit_acadp_field,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_acadp_listing,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_acadp_payment,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_achievement_events,achievements,300,Achievements for WordPress +edit_achievement_progresses,achievements,300,Achievements for WordPress +edit_achievements,achievements,300,Achievements for WordPress +edit_ad_terms,apply-online,5000,Apply Online +edit_admincolorschemes,easy-admin-color-schemes,1000,Easy Admin Color Schemes +edit_ads,apply-online,5000,Apply Online +edit_aec_event,another-events-calendar,800,Another Events Calendar +edit_aec_events,another-events-calendar,800,Another Events Calendar +edit_aec_organizer,another-events-calendar,800,Another Events Calendar +edit_aec_organizers,another-events-calendar,800,Another Events Calendar +edit_aec_venue,another-events-calendar,800,Another Events Calendar +edit_aec_venues,another-events-calendar,800,Another Events Calendar +edit_affiliate_keywords,affiliate,700,Affiliate +edit_agent,essential-real-estate,3000,Essential Real Estate +edit_agent_terms,essential-real-estate,3000,Essential Real Estate +edit_agents,essential-real-estate,3000,Essential Real Estate +edit_aggregator-records,the-events-calendar,700000,The Events Calendar +edit_ai1ec_event,all-in-one-event-calendar,100000,All-in-One Event Calendar +edit_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +edit_aiovg_video,all-in-one-video-gallery,1000,All-in-One Video Gallery +edit_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +edit_all_touchpoints,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +edit_all_ukuupeoples,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +edit_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +edit_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +edit_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +edit_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +edit_anbs,alert-notice-boxes,1000,Alert Notice Boxes +edit_application,apply-online,5000,Apply Online +edit_applications,apply-online,5000,Apply Online +edit_archiv,archive,700,Archive +edit_archive_structure,archive,700,Archive +edit_archivs,archive,700,Archive +edit_article,issuem,1000,IssueM +edit_articles,issuem,1000,IssueM +edit_at_biz_dir,directorist,500,Directorist – Business Directory Plugin +edit_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +edit_atbdp_order,directorist,500,Directorist – Business Directory Plugin +edit_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +edit_attachments,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_awebooking,awebooking,6000,AweBooking – Hotel Booking System +edit_awebooking_terms,awebooking,6000,AweBooking – Hotel Booking System +edit_awebookings,awebooking,6000,AweBooking – Hotel Booking System +edit_birs_appointment,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_client,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_location,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_payment,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_service,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_staff,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_blocks,gutenberg,500000,Gutenberg +edit_board_committees,nonprofit-board-management,400,Nonprofit Board Management +edit_board_content,nonprofit-board-management,400,Nonprofit Board Management +edit_board_events,nonprofit-board-management,400,Nonprofit Board Management +edit_book,novelist,800,Novelist +edit_book-reviews,book-review-library,700,Book Review Library +edit_book_terms,novelist,800,Novelist +edit_books,novelist,800,Novelist +edit_box,boxzilla,20000,Boxzilla +edit_boxes,boxzilla,20000,Boxzilla +edit_bps_forms,bp-profile-search,10000,BP Profile Search +edit_by_site_editor,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +edit_calp_event,calpress-event-calendar,5000,CalPress Calendar +edit_calp_events,calpress-event-calendar,5000,CalPress Calendar +edit_campaign,charitable,10000,Charitable – Donation Plugin +edit_campaign,leyka,1000,Leyka +edit_campaign,personal-fundraiser,200,Personal Fundraiser +edit_campaign_terms,charitable,10000,Charitable – Donation Plugin +edit_campaigns,charitable,10000,Charitable – Donation Plugin +edit_campaigns,leyka,1000,Leyka +edit_cannedresponse_category,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_cannedresponse_tag,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_car_listing,wp-car-manager,3000,WP Car Manager +edit_car_listing_terms,wp-car-manager,3000,WP Car Manager +edit_car_listings,wp-car-manager,3000,WP Car Manager +edit_categories,projectmanager,400,ProjectManager +edit_cbxaccounting,cbxwpsimpleaccounting,300,CBX Accounting +edit_cctor_coupon,coupon-creator,10000,Coupon Creator +edit_cctor_coupons,coupon-creator,10000,Coupon Creator +edit_chronosly,chronosly-events-calendar,4000,Chronosly Events Calendar +edit_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +edit_classified_listing,classifieds-wp,800,Classifieds WP +edit_classified_listing_terms,classifieds-wp,800,Classifieds WP +edit_classified_listings,classifieds-wp,800,Classifieds WP +edit_client,upstream,1000,WordPress Project Management by UpStream +edit_client_terms,upstream,1000,WordPress Project Management by UpStream +edit_clients,upstream,1000,WordPress Project Management by UpStream +edit_comment,wpsite-comment-moderator,200,Comment Moderator +edit_contact_country,wp-easy-contact,600,Best Contact Management Software for WordPress +edit_contact_state,wp-easy-contact,600,Best Contact Management Software for WordPress +edit_contact_tag,wp-easy-contact,600,Best Contact Management Software for WordPress +edit_contact_topic,wp-easy-contact,600,Best Contact Management Software for WordPress +edit_content_shortcodes,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_cooked_recipes,cooked,4000,Cooked – Recipe Plugin +edit_cooked_settings,cooked,4000,Cooked – Recipe Plugin +edit_course,lifterlms,8000,LifterLMS +edit_course_cats,lifterlms,8000,LifterLMS +edit_course_difficulties,lifterlms,8000,LifterLMS +edit_course_tags,lifterlms,8000,LifterLMS +edit_course_tracks,lifterlms,8000,LifterLMS +edit_courses,lifterlms,8000,LifterLMS +edit_crossword,crosswordsearch,300,crosswordsearch +edit_cta,cta,10000,WordPress Calls to Action +edit_ctas,cta,10000,WordPress Calls to Action +edit_cupri_pay,pardakht-delkhah,1000,پلاگین پرداخت دلخواه +edit_cupri_pays,pardakht-delkhah,1000,پلاگین پرداخت دلخواه +edit_custom_css,custom-css-js,100000,Simple Custom CSS and JS +edit_custom_csss,custom-css-js,100000,Simple Custom CSS and JS +edit_customfields,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +edit_dataset_order,projectmanager,400,ProjectManager +edit_datasets,projectmanager,400,ProjectManager +edit_departments,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_ditty_news_ticker,ditty-news-ticker,40000,Ditty News Ticker +edit_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +edit_documents,wp-document-revisions,4000,WP Document Revisions +edit_domain_check,domain-check,500,Domain Check +edit_donation,charitable,10000,Charitable – Donation Plugin +edit_donation,leyka,1000,Leyka +edit_donations,charitable,10000,Charitable – Donation Plugin +edit_donations,leyka,1000,Leyka +edit_dsn_note,admin-dashboard-site-notes,3000,Dashboard Site Notes +edit_dsn_notes,admin-dashboard-site-notes,3000,Dashboard Site Notes +edit_edr_course,educator,1000,Educator 2 +edit_edr_courses,educator,1000,Educator 2 +edit_edr_lesson,educator,1000,Educator 2 +edit_edr_lessons,educator,1000,Educator 2 +edit_edr_membership,educator,1000,Educator 2 +edit_edr_memberships,educator,1000,Educator 2 +edit_email_categories,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +edit_email_template,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +edit_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +edit_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_emd_employees,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +edit_emd_persons,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +edit_emd_quotes,request-a-quote,1000,Request a Quote +edit_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_emd_videos,youtube-showcase,7000,YouTube Gallery – Best YouTube Video Gallery for WordPress +edit_employee,hrm,200,WP Human Resource Management +edit_employee_tags,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +edit_employment_type,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_epa_albums,easy-photo-album,5000,Easy Photo Album +edit_event,quick-event-manager,5000,Quick Event Manager +edit_event_categories,events-manager,100000,Events Manager +edit_event_listing,wp-event-manager,1000,WP Event Manager +edit_event_listing_terms,wp-event-manager,1000,WP Event Manager +edit_event_listings,wp-event-manager,1000,WP Event Manager +edit_event_magic,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +edit_event_magic_terms,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +edit_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +edit_events,event-organiser,40000,Event Organiser +edit_events,events-maker,4000,Events Maker by dFactory +edit_events,events-manager,100000,Events Manager +edit_events,quick-event-manager,5000,Quick Event Manager +edit_everest_form,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +edit_everest_form_terms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +edit_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +edit_fa_items,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +edit_fa_terms,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +edit_fancy_news,fancy-news,300,Fancy News +edit_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +edit_feed,wp-rss-aggregator,60000,WP RSS Aggregator +edit_feed_source,wp-rss-aggregator,60000,WP RSS Aggregator +edit_feed_source_terms,wp-rss-aggregator,60000,WP RSS Aggregator +edit_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +edit_feed_terms,wp-rss-aggregator,60000,WP RSS Aggregator +edit_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +edit_fep_announcements,front-end-pm,8000,Front End PM +edit_fep_messages,front-end-pm,8000,Front End PM +edit_filter_group,plugin-organizer,10000,Plugin Organizer +edit_flexible_invoice,flexible-invoices,1000,Flexible Invoices for WordPress +edit_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +edit_food_group,restaurantpress,3000,RestaurantPress +edit_food_group_terms,restaurantpress,3000,RestaurantPress +edit_food_groups,restaurantpress,3000,RestaurantPress +edit_food_menu,restaurantpress,3000,RestaurantPress +edit_food_menu_terms,restaurantpress,3000,RestaurantPress +edit_food_menus,restaurantpress,3000,RestaurantPress +edit_footer_text,footer-text,10000,Footer Text +edit_form,formlift,800,FormLift for Infusionsoft Web Forms +edit_form,pronamic-ideal,6000,Pronamic Pay +edit_formfields,projectmanager,400,ProjectManager +edit_forms,formlift,800,FormLift for Infusionsoft Web Forms +edit_forms,html-forms,1000,HTML Forms +edit_forms,pronamic-ideal,6000,Pronamic Pay +edit_forums,bbpress,300000,bbPress +edit_galleries,gallery-box,2000,Gallery Box +edit_gallery,gallery-box,2000,Gallery Box +edit_game,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_gender,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_gigya,gigya-socialize-for-wordpress,200,Gigya – Social Infrastructure +edit_gigya_secret,gigya-socialize-for-wordpress,200,Gigya – Social Infrastructure +edit_give_form,give,50000,Give – Donation Plugin and Fundraising Platform +edit_give_form_terms,give,50000,Give – Donation Plugin and Fundraising Platform +edit_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +edit_give_payment,give,50000,Give – Donation Plugin and Fundraising Platform +edit_give_payment_terms,give,50000,Give – Donation Plugin and Fundraising Platform +edit_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +edit_glossaries,glossary-by-codeat,1000,Glossary +edit_glossary,glossary-by-codeat,1000,Glossary +edit_groups,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +edit_h5p_contents,h5p,10000,Interactive Content – H5P +edit_hanaboard-post,hana-board,1000,Hana-Board 하나보드 워드프레스 게시판 +edit_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +edit_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +edit_hf_membership_plan,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_hf_user_membership,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_hotel_location,awebooking,6000,AweBooking – Hotel Booking System +edit_hotel_location_terms,awebooking,6000,AweBooking – Hotel Booking System +edit_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +edit_hotel_service,awebooking,6000,AweBooking – Hotel Booking System +edit_hotel_service_terms,awebooking,6000,AweBooking – Hotel Booking System +edit_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +edit_ib_edu_membership,ibeducator,1000,Educator +edit_ib_edu_memberships,ibeducator,1000,Educator +edit_ib_educator_course,ibeducator,1000,Educator +edit_ib_educator_courses,ibeducator,1000,Educator +edit_ib_educator_lesson,ibeducator,1000,Educator +edit_ib_educator_lessons,ibeducator,1000,Educator +edit_ims_gallery,image-store,900,Image Store +edit_ims_gallerys,image-store,900,Image Store +edit_in_section,bu-section-editing,300,BU Section Editing +edit_inbound-form,cta,10000,WordPress Calls to Action +edit_inbound-form,landing-pages,10000,WordPress Landing Pages +edit_inbound-form,leads,7000,WordPress Leads +edit_inbound-forms,cta,10000,WordPress Calls to Action +edit_inbound-forms,landing-pages,10000,WordPress Landing Pages +edit_inbound-forms,leads,7000,WordPress Leads +edit_insertcode,insert-code,300,Insert Code +edit_insertcodes,insert-code,300,Insert Code +edit_invoice,essential-real-estate,3000,Essential Real Estate +edit_invoice_terms,essential-real-estate,3000,Essential Real Estate +edit_invoices,essential-real-estate,3000,Essential Real Estate +edit_issues,issuem,1000,IssueM +edit_item,gamipress,2000,GamiPress +edit_items,gamipress,2000,GamiPress +edit_jbbrd_businesses_tags,job-board,300,Job Board by BestWebSoft +edit_jbbrd_employment_tags,job-board,300,Job Board by BestWebSoft +edit_job_listing,wp-job-manager,100000,WP Job Manager +edit_job_listing_terms,wp-job-manager,100000,WP Job Manager +edit_job_listings,wp-job-manager,100000,WP Job Manager +edit_jobtitles,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_jscp_match,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_jscp_matchs,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_jscp_player,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_jscp_players,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_jscp_team,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_jscp_teams,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_klaviyo_shop_cart,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +edit_klaviyo_shop_cart_terms,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +edit_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +edit_landing_page,landing-pages,10000,WordPress Landing Pages +edit_landing_pages,landing-pages,10000,WordPress Landing Pages +edit_language,sublanguage,1000,Sublanguage +edit_languages,sublanguage,1000,Sublanguage +edit_lazyest_fields,lazyest-gallery,1000,Lazyest Gallery +edit_lead,cta,10000,WordPress Calls to Action +edit_lead,landing-pages,10000,WordPress Landing Pages +edit_lead,leads,7000,WordPress Leads +edit_leads,cta,10000,WordPress Calls to Action +edit_leads,landing-pages,10000,WordPress Landing Pages +edit_leads,leads,7000,WordPress Leads +edit_league_settings,leaguemanager,2000,LeagueManager +edit_leagues,leaguemanager,2000,LeagueManager +edit_legalpack_pages,legalpack,400,Legalpack +edit_lesson,lifterlms,8000,LifterLMS +edit_lessons,lifterlms,8000,LifterLMS +edit_listing,auto-listings,400,Auto Listings +edit_listing,wp-real-estate,400,WP Real Estate +edit_listing,wpcasa,2000,WPCasa +edit_listing_id,wpcasa,2000,WPCasa +edit_listing_terms,wpcasa,2000,WPCasa +edit_listings,auto-listings,400,Auto Listings +edit_listings,wp-real-estate,400,WP Real Estate +edit_listings,wpcasa,2000,WPCasa +edit_locations,events-manager,100000,Events Manager +edit_login_redirects,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_marital_status,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_matches,leaguemanager,2000,LeagueManager +edit_mbdb_book,mooberry-book-manager,1000,Mooberry Book Manager +edit_mbdb_book_grid,mooberry-book-manager,1000,Mooberry Book Manager +edit_mbdb_book_grids,mooberry-book-manager,1000,Mooberry Book Manager +edit_mbdb_books,mooberry-book-manager,1000,Mooberry Book Manager +edit_meals,restaurant-manager,800,Restaurant Manager +edit_membership,lifterlms,8000,LifterLMS +edit_membership_cats,lifterlms,8000,LifterLMS +edit_membership_tags,lifterlms,8000,LifterLMS +edit_memberships,lifterlms,8000,LifterLMS +edit_mp_menu_item,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_mp_menu_item_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_mprm_order,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_mprm_order_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_mstw_tr_settings,team-rosters,800,Team Rosters +edit_nav_menu_permissions,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_nc_reference,nelio-content,7000,Nelio Content – Social Media Marketing Automation +edit_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +edit_nemus-sliders,nemus-slider,2000,Nemus Slider +edit_news,news-manager,3000,News Manager +edit_newsletters,alo-easymail,10000,ALO EasyMail Newsletter +edit_niso_others_carousels_slider,niso-carousel,200,Niso Carousel +edit_niso_others_carousels_slider,niso-carousel-slider,400,Niso Carousel Slider +edit_niso_slider_carousel,niso-carousel,200,Niso Carousel +edit_niso_slider_carousel,niso-carousel-slider,400,Niso Carousel Slider +edit_niso_slider_carousels,niso-carousel,200,Niso Carousel +edit_niso_slider_carousels,niso-carousel-slider,400,Niso Carousel Slider +edit_office_locations,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +edit_opalestate_agents,opal-estate,1000,Opal Estate +edit_opalestate_agents_terms,opal-estate,1000,Opal Estate +edit_opalestate_agentss,opal-estate,1000,Opal Estate +edit_opalestate_properties,opal-estate,1000,Opal Estate +edit_opalestate_properties_terms,opal-estate,1000,Opal Estate +edit_opalestate_propertiess,opal-estate,1000,Opal Estate +edit_opanda-item,opt-in-panda,3000,OnePress Opt-In Panda +edit_opanda-item,social-locker,10000,OnePress Social Locker +edit_opanda-items,opt-in-panda,3000,OnePress Opt-In Panda +edit_opanda-items,social-locker,10000,OnePress Social Locker +edit_orbis_companies,orbis,200,Orbis +edit_orbis_company,orbis,200,Orbis +edit_orbis_project,orbis,200,Orbis +edit_orbis_projects,orbis,200,Orbis +edit_other_boxes,boxzilla,20000,Boxzilla +edit_other_datasets,projectmanager,400,ProjectManager +edit_other_languages,sublanguage,1000,Sublanguage +edit_other_portfolios,visual-portfolio,7000,Visual Portfolio +edit_other_posts,baw-moderator-role,300,Moderator Role +edit_other_sola_st_tickets,sola-support-tickets,400,Sola Support Tickets +edit_other_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +edit_other_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +edit_other_wplc_quick_response,wp-live-chat-support,60000,WP Live Chat Support +edit_others_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_others_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_others_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_others_achievement_progresses,achievements,300,Achievements for WordPress +edit_others_achievements,achievements,300,Achievements for WordPress +edit_others_admincolorschemes,easy-admin-color-schemes,1000,Easy Admin Color Schemes +edit_others_ads,apply-online,5000,Apply Online +edit_others_aec_events,another-events-calendar,800,Another Events Calendar +edit_others_aec_organizers,another-events-calendar,800,Another Events Calendar +edit_others_aec_venues,another-events-calendar,800,Another Events Calendar +edit_others_affiliate_keywords,affiliate,700,Affiliate +edit_others_agents,essential-real-estate,3000,Essential Real Estate +edit_others_aggregator-records,the-events-calendar,700000,The Events Calendar +edit_others_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +edit_others_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +edit_others_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +edit_others_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +edit_others_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +edit_others_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +edit_others_anbs,alert-notice-boxes,1000,Alert Notice Boxes +edit_others_applications,apply-online,5000,Apply Online +edit_others_archivs,archive,700,Archive +edit_others_articles,issuem,1000,IssueM +edit_others_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +edit_others_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +edit_others_attachments,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_others_awebookings,awebooking,6000,AweBooking – Hotel Booking System +edit_others_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_others_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_others_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_others_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_others_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_others_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_others_blocks,gutenberg,500000,Gutenberg +edit_others_board_committees,nonprofit-board-management,400,Nonprofit Board Management +edit_others_board_events,nonprofit-board-management,400,Nonprofit Board Management +edit_others_book-reviews,book-review-library,700,Book Review Library +edit_others_books,novelist,800,Novelist +edit_others_bps_forms,bp-profile-search,10000,BP Profile Search +edit_others_calp_events,calpress-event-calendar,5000,CalPress Calendar +edit_others_campaigns,charitable,10000,Charitable – Donation Plugin +edit_others_campaigns,leyka,1000,Leyka +edit_others_car_listings,wp-car-manager,3000,WP Car Manager +edit_others_cctor_coupons,coupon-creator,10000,Coupon Creator +edit_others_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +edit_others_classified_listings,classifieds-wp,800,Classifieds WP +edit_others_clients,upstream,1000,WordPress Project Management by UpStream +edit_others_courses,lifterlms,8000,LifterLMS +edit_others_ctas,cta,10000,WordPress Calls to Action +edit_others_cupri_pays,pardakht-delkhah,1000,پلاگین پرداخت دلخواه +edit_others_custom_csss,custom-css-js,100000,Simple Custom CSS and JS +edit_others_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +edit_others_documents,wp-document-revisions,4000,WP Document Revisions +edit_others_donations,charitable,10000,Charitable – Donation Plugin +edit_others_donations,leyka,1000,Leyka +edit_others_dsn_notes,admin-dashboard-site-notes,3000,Dashboard Site Notes +edit_others_edr_courses,educator,1000,Educator 2 +edit_others_edr_lessons,educator,1000,Educator 2 +edit_others_edr_memberships,educator,1000,Educator 2 +edit_others_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_others_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +edit_others_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_others_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_others_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +edit_others_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_others_emd_quotes,request-a-quote,1000,Request a Quote +edit_others_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_others_epa_albums,easy-photo-album,5000,Easy Photo Album +edit_others_event_listings,wp-event-manager,1000,WP Event Manager +edit_others_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +edit_others_events,event-organiser,40000,Event Organiser +edit_others_events,events-maker,4000,Events Maker by dFactory +edit_others_events,events-manager,100000,Events Manager +edit_others_events,quick-event-manager,5000,Quick Event Manager +edit_others_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +edit_others_fa_items,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +edit_others_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +edit_others_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +edit_others_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +edit_others_fep_announcements,front-end-pm,8000,Front End PM +edit_others_fep_messages,front-end-pm,8000,Front End PM +edit_others_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +edit_others_food_groups,restaurantpress,3000,RestaurantPress +edit_others_food_menus,restaurantpress,3000,RestaurantPress +edit_others_forms,pronamic-ideal,6000,Pronamic Pay +edit_others_forums,bbpress,300000,bbPress +edit_others_galleries,gallery-box,2000,Gallery Box +edit_others_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_others_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +edit_others_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +edit_others_glossaries,glossary-by-codeat,1000,Glossary +edit_others_h5p_contents,h5p,10000,Interactive Content – H5P +edit_others_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +edit_others_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +edit_others_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_others_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_others_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +edit_others_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +edit_others_ib_edu_memberships,ibeducator,1000,Educator +edit_others_ib_educator_courses,ibeducator,1000,Educator +edit_others_ib_educator_lessons,ibeducator,1000,Educator +edit_others_ims_gallerys,image-store,900,Image Store +edit_others_inbound-forms,cta,10000,WordPress Calls to Action +edit_others_inbound-forms,landing-pages,10000,WordPress Landing Pages +edit_others_inbound-forms,leads,7000,WordPress Leads +edit_others_insertcodes,insert-code,300,Insert Code +edit_others_invoices,essential-real-estate,3000,Essential Real Estate +edit_others_issues,issuem,1000,IssueM +edit_others_items,gamipress,2000,GamiPress +edit_others_job_listings,wp-job-manager,100000,WP Job Manager +edit_others_jscp_match,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_others_jscp_player,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_others_jscp_team,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_others_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +edit_others_landing_pages,landing-pages,10000,WordPress Landing Pages +edit_others_leads,cta,10000,WordPress Calls to Action +edit_others_leads,landing-pages,10000,WordPress Landing Pages +edit_others_leads,leads,7000,WordPress Leads +edit_others_legalpack_pages,legalpack,400,Legalpack +edit_others_lessons,lifterlms,8000,LifterLMS +edit_others_listings,auto-listings,400,Auto Listings +edit_others_listings,wp-real-estate,400,WP Real Estate +edit_others_listings,wpcasa,2000,WPCasa +edit_others_locations,events-manager,100000,Events Manager +edit_others_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_others_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_others_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_others_mbdb_book,mooberry-book-manager,1000,Mooberry Book Manager +edit_others_mbdb_book_grid,mooberry-book-manager,1000,Mooberry Book Manager +edit_others_mbdb_book_grids,mooberry-book-manager,1000,Mooberry Book Manager +edit_others_mbdb_books,mooberry-book-manager,1000,Mooberry Book Manager +edit_others_meals,restaurant-manager,800,Restaurant Manager +edit_others_memberships,lifterlms,8000,LifterLMS +edit_others_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_others_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_others_nc_reference,nelio-content,7000,Nelio Content – Social Media Marketing Automation +edit_others_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +edit_others_nemus-sliders,nemus-slider,2000,Nemus Slider +edit_others_news,news-manager,3000,News Manager +edit_others_newsletters,alo-easymail,10000,ALO EasyMail Newsletter +edit_others_opalestate_agentss,opal-estate,1000,Opal Estate +edit_others_opalestate_propertiess,opal-estate,1000,Opal Estate +edit_others_opanda-items,opt-in-panda,3000,OnePress Opt-In Panda +edit_others_opanda-items,social-locker,10000,OnePress Social Locker +edit_others_orbis_companies,orbis,200,Orbis +edit_others_orbis_projects,orbis,200,Orbis +edit_others_packages,essential-real-estate,3000,Essential Real Estate +edit_others_payments,pronamic-ideal,6000,Pronamic Pay +edit_others_players,team-rosters,800,Team Rosters +edit_others_playlists,radio-station,1000,Radio Station +edit_others_plugin_filters,plugin-organizer,10000,Plugin Organizer +edit_others_plugin_groups,plugin-organizer,10000,Plugin Organizer +edit_others_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +edit_others_portfolios,flash-toolkit,30000,Flash Toolkit +edit_others_portfolios,suffice-toolkit,5000,Suffice Toolkit +edit_others_posts_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_others_posts_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_others_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +edit_others_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +edit_others_products,design-approval-system,500,Design Approval System +edit_others_products,easy-digital-downloads,60000,Easy Digital Downloads +edit_others_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +edit_others_products,gnucommerce,1000,GNUCommerce +edit_others_products,jigoshop,4000,Jigoshop +edit_others_products,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_others_products,post-type-x,1000,Product Catalog X +edit_others_products,products,300,WP Products +edit_others_products,webmaster-user-role,8000,Webmaster User Role +edit_others_products,woocommerce,4000000,WooCommerce +edit_others_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_others_profile_cct,profile-custom-content-type,200,Profile CCT +edit_others_projects,upstream,1000,WordPress Project Management by UpStream +edit_others_propertys,essential-real-estate,3000,Essential Real Estate +edit_others_psp_projects,project-panorama-lite,1000,Project Panorama +edit_others_questions,lifterlms,8000,LifterLMS +edit_others_quizzes,lifterlms,8000,LifterLMS +edit_others_quotes,mg-quotes,300,mg Quotes +edit_others_recurring_events,events-manager,100000,Events Manager +edit_others_redirects,wp-redirects,700,WP Redirects +edit_others_rem_properties,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +edit_others_replies,bbpress,300000,bbPress +edit_others_reservations,restaurant-manager,800,Restaurant Manager +edit_others_resume,wp-resume,700,WP Resume +edit_others_resume_positions,wp-resume,700,WP Resume +edit_others_room_reservations,wp-hotelier,1000,Easy WP Hotelier +edit_others_room_types,awebooking,6000,AweBooking – Hotel Booking System +edit_others_rooms,wp-hotelier,1000,Easy WP Hotelier +edit_others_rsvpemails,rsvpmaker,1000,RSVPMaker +edit_others_rsvpmakers,rsvpmaker,1000,RSVPMaker +edit_others_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_others_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +edit_others_shifts,employee-scheduler,400,Shiftee Basic – Employee and Staff Scheduling +edit_others_shop_coupons,jigoshop,4000,Jigoshop +edit_others_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_others_shop_coupons,webmaster-user-role,8000,Webmaster User Role +edit_others_shop_coupons,woocommerce,4000000,WooCommerce +edit_others_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +edit_others_shop_emails,jigoshop,4000,Jigoshop +edit_others_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_others_shop_orders,jigoshop,4000,Jigoshop +edit_others_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_others_shop_orders,webmaster-user-role,8000,Webmaster User Role +edit_others_shop_orders,woocommerce,4000000,WooCommerce +edit_others_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +edit_others_shop_webhooks,woocommerce,4000000,WooCommerce +edit_others_shows,radio-station,1000,Radio Station +edit_others_sln_attendants,salon-booking-system,5000,Salon booking system +edit_others_sln_bookings,salon-booking-system,5000,Salon booking system +edit_others_sln_services,salon-booking-system,5000,Salon booking system +edit_others_snippets,wp-snippets,1000,WP Snippets +edit_others_snitchs,snitch,1000,Snitch +edit_others_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +edit_others_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +edit_others_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +edit_others_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +edit_others_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +edit_others_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +edit_others_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +edit_others_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +edit_others_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_others_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +edit_others_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_others_stores,wp-store-locator,50000,WP Store Locator +edit_others_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_others_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_others_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_others_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_others_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_others_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_others_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_others_tm-propertys,cherry-real-estate,600,Cherry Real Estate +edit_others_topics,bbpress,300000,bbPress +edit_others_total_slider_slides,total-slider,500,Total Slider +edit_others_trans_logs,essential-real-estate,3000,Essential Real Estate +edit_others_translations,simple-punctual-translation,200,Simple Punctual Translation +edit_others_tribe_events,the-events-calendar,700000,The Events Calendar +edit_others_tribe_organizers,the-events-calendar,700000,The Events Calendar +edit_others_tribe_venues,the-events-calendar,700000,The Events Calendar +edit_others_un_feedback_items,usernoise,7000,Usernoise modal feedback / contact form +edit_others_user_packages,essential-real-estate,3000,Essential Real Estate +edit_others_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +edit_others_vacancies,job-board,300,Job Board by BestWebSoft +edit_others_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_others_video_encodes,video-embed-thumbnail-generator,30000,Video Embed & Thumbnail Generator +edit_others_wbcr-snippetss,insert-php,100000,PHP code snippets (Insert PHP) +edit_others_wctrl_contents,widgets-control,1000,Widgets Control +edit_others_wd_ads_adverts,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +edit_others_wd_ads_schedules,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +edit_others_wiki_pages,wordpress-wiki,400,WordPress Wiki +edit_others_wordlift_entities,wordlift,400,WordLift – AI powered SEO +edit_others_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +edit_others_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_others_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_others_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_others_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_others_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_others_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +edit_others_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +edit_others_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_others_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_others_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_others_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_others_wpp_properties,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +edit_others_wppizzas,wppizza,2000,WPPizza +edit_others_wprm_reservations,wp-restaurant-manager,700,WP Restaurant Manager +edit_others_wpsdealss,deals-engine,200,Social Deals Engine +edit_others_wpsdealssaless,deals-engine,200,Social Deals Engine +edit_others_wpse_profiles,wp-smart-editor,900,WP Smart Editor +edit_others_wswebinars,wp-webinarsystem,2000,WP WebinarSystem +edit_others_ycd_countdowns,countdown-builder,1000,Countdown +edit_own_touchpoints,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +edit_own_ukuupeoples,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +edit_own_yop_polls,yop-poll,20000,YOP Poll +edit_own_yop_polls_templates,yop-poll,20000,YOP Poll +edit_package,essential-real-estate,3000,Essential Real Estate +edit_package_terms,essential-real-estate,3000,Essential Real Estate +edit_packages,essential-real-estate,3000,Essential Real Estate +edit_page_in_section,bu-section-editing,300,BU Section Editing +edit_pages_role_permissions,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_payment,pronamic-ideal,6000,Pronamic Pay +edit_payments,pronamic-ideal,6000,Pronamic Pay +edit_person_area,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +edit_person_location,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +edit_person_rareas,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +edit_person_title,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +edit_player,team-rosters,800,Team Rosters +edit_players,team-rosters,800,Team Rosters +edit_playlists,radio-station,1000,Radio Station +edit_plugin_filter,plugin-organizer,10000,Plugin Organizer +edit_plugin_filters,plugin-organizer,10000,Plugin Organizer +edit_plugin_group,plugin-organizer,10000,Plugin Organizer +edit_plugin_groups,plugin-organizer,10000,Plugin Organizer +edit_portfolio,flash-toolkit,30000,Flash Toolkit +edit_portfolio,suffice-toolkit,5000,Suffice Toolkit +edit_portfolio,visual-portfolio,7000,Visual Portfolio +edit_portfolio_categories,custom-content-portfolio,1000,Custom Content Portfolio +edit_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +edit_portfolio_tags,custom-content-portfolio,1000,Custom Content Portfolio +edit_portfolio_terms,flash-toolkit,30000,Flash Toolkit +edit_portfolio_terms,suffice-toolkit,5000,Suffice Toolkit +edit_portfolios,flash-toolkit,30000,Flash Toolkit +edit_portfolios,suffice-toolkit,5000,Suffice Toolkit +edit_portfolios,visual-portfolio,7000,Visual Portfolio +edit_post,realia,4000,Realia +edit_post,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +edit_post_in_section,bu-section-editing,300,BU Section Editing +edit_post_subscriptions,edit-flow,10000,Edit Flow +edit_post_subscriptions,publishpress,1000,PublishPress – Professional publishing tools for WordPress +edit_posts_role_permissions,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_pricing_rate,awebooking,6000,AweBooking – Hotel Booking System +edit_pricing_rate_terms,awebooking,6000,AweBooking – Hotel Booking System +edit_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +edit_private_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_private_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_private_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_private_ads,apply-online,5000,Apply Online +edit_private_aec_events,another-events-calendar,800,Another Events Calendar +edit_private_aec_organizers,another-events-calendar,800,Another Events Calendar +edit_private_aec_venues,another-events-calendar,800,Another Events Calendar +edit_private_affiliate_keywords,affiliate,700,Affiliate +edit_private_agents,essential-real-estate,3000,Essential Real Estate +edit_private_aggregator-records,the-events-calendar,700000,The Events Calendar +edit_private_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +edit_private_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +edit_private_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +edit_private_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +edit_private_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +edit_private_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +edit_private_anbs,alert-notice-boxes,1000,Alert Notice Boxes +edit_private_applications,apply-online,5000,Apply Online +edit_private_archivs,archive,700,Archive +edit_private_articles,issuem,1000,IssueM +edit_private_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +edit_private_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +edit_private_awebookings,awebooking,6000,AweBooking – Hotel Booking System +edit_private_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_private_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_private_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_private_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_private_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_private_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_private_blocks,gutenberg,500000,Gutenberg +edit_private_board_committees,nonprofit-board-management,400,Nonprofit Board Management +edit_private_board_events,nonprofit-board-management,400,Nonprofit Board Management +edit_private_books,novelist,800,Novelist +edit_private_calp_events,calpress-event-calendar,5000,CalPress Calendar +edit_private_campaigns,charitable,10000,Charitable – Donation Plugin +edit_private_campaigns,leyka,1000,Leyka +edit_private_car_listings,wp-car-manager,3000,WP Car Manager +edit_private_cctor_coupons,coupon-creator,10000,Coupon Creator +edit_private_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +edit_private_classified_listings,classifieds-wp,800,Classifieds WP +edit_private_clients,upstream,1000,WordPress Project Management by UpStream +edit_private_courses,lifterlms,8000,LifterLMS +edit_private_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +edit_private_documents,wp-document-revisions,4000,WP Document Revisions +edit_private_donations,charitable,10000,Charitable – Donation Plugin +edit_private_donations,leyka,1000,Leyka +edit_private_edr_courses,educator,1000,Educator 2 +edit_private_edr_lessons,educator,1000,Educator 2 +edit_private_edr_memberships,educator,1000,Educator 2 +edit_private_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_private_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +edit_private_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_private_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_private_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +edit_private_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_private_emd_quotes,request-a-quote,1000,Request a Quote +edit_private_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_private_epa_albums,easy-photo-album,5000,Easy Photo Album +edit_private_event_listings,wp-event-manager,1000,WP Event Manager +edit_private_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +edit_private_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +edit_private_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +edit_private_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +edit_private_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +edit_private_fep_announcements,front-end-pm,8000,Front End PM +edit_private_fep_messages,front-end-pm,8000,Front End PM +edit_private_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +edit_private_food_groups,restaurantpress,3000,RestaurantPress +edit_private_food_menus,restaurantpress,3000,RestaurantPress +edit_private_forms,pronamic-ideal,6000,Pronamic Pay +edit_private_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_private_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +edit_private_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +edit_private_glossaries,glossary-by-codeat,1000,Glossary +edit_private_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +edit_private_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +edit_private_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_private_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_private_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +edit_private_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +edit_private_ib_edu_memberships,ibeducator,1000,Educator +edit_private_ib_educator_courses,ibeducator,1000,Educator +edit_private_ib_educator_lessons,ibeducator,1000,Educator +edit_private_invoices,essential-real-estate,3000,Essential Real Estate +edit_private_job_listings,wp-job-manager,100000,WP Job Manager +edit_private_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +edit_private_legalpack_pages,legalpack,400,Legalpack +edit_private_lessons,lifterlms,8000,LifterLMS +edit_private_listings,auto-listings,400,Auto Listings +edit_private_listings,wp-real-estate,400,WP Real Estate +edit_private_listings,wpcasa,2000,WPCasa +edit_private_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_private_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_private_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_private_meals,restaurant-manager,800,Restaurant Manager +edit_private_memberships,lifterlms,8000,LifterLMS +edit_private_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_private_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_private_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +edit_private_nemus-sliders,nemus-slider,2000,Nemus Slider +edit_private_opalestate_agentss,opal-estate,1000,Opal Estate +edit_private_opalestate_propertiess,opal-estate,1000,Opal Estate +edit_private_packages,essential-real-estate,3000,Essential Real Estate +edit_private_payments,pronamic-ideal,6000,Pronamic Pay +edit_private_players,team-rosters,800,Team Rosters +edit_private_playlists,radio-station,1000,Radio Station +edit_private_plugin_filters,plugin-organizer,10000,Plugin Organizer +edit_private_plugin_groups,plugin-organizer,10000,Plugin Organizer +edit_private_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +edit_private_portfolios,flash-toolkit,30000,Flash Toolkit +edit_private_portfolios,suffice-toolkit,5000,Suffice Toolkit +edit_private_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +edit_private_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +edit_private_products,design-approval-system,500,Design Approval System +edit_private_products,easy-digital-downloads,60000,Easy Digital Downloads +edit_private_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +edit_private_products,gnucommerce,1000,GNUCommerce +edit_private_products,jigoshop,4000,Jigoshop +edit_private_products,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_private_products,post-type-x,1000,Product Catalog X +edit_private_products,products,300,WP Products +edit_private_products,webmaster-user-role,8000,Webmaster User Role +edit_private_products,woocommerce,4000000,WooCommerce +edit_private_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_private_projects,upstream,1000,WordPress Project Management by UpStream +edit_private_propertys,essential-real-estate,3000,Essential Real Estate +edit_private_questions,lifterlms,8000,LifterLMS +edit_private_quizzes,lifterlms,8000,LifterLMS +edit_private_quotes,mg-quotes,300,mg Quotes +edit_private_redirects,wp-redirects,700,WP Redirects +edit_private_reservations,restaurant-manager,800,Restaurant Manager +edit_private_resume_positions,wp-resume,700,WP Resume +edit_private_room_reservations,wp-hotelier,1000,Easy WP Hotelier +edit_private_room_types,awebooking,6000,AweBooking – Hotel Booking System +edit_private_rooms,wp-hotelier,1000,Easy WP Hotelier +edit_private_rsvpmakers,rsvpmaker,1000,RSVPMaker +edit_private_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_private_shop_coupons,jigoshop,4000,Jigoshop +edit_private_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_private_shop_coupons,webmaster-user-role,8000,Webmaster User Role +edit_private_shop_coupons,woocommerce,4000000,WooCommerce +edit_private_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +edit_private_shop_emails,jigoshop,4000,Jigoshop +edit_private_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_private_shop_orders,jigoshop,4000,Jigoshop +edit_private_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_private_shop_orders,webmaster-user-role,8000,Webmaster User Role +edit_private_shop_orders,woocommerce,4000000,WooCommerce +edit_private_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +edit_private_shop_webhooks,woocommerce,4000000,WooCommerce +edit_private_shows,radio-station,1000,Radio Station +edit_private_sln_attendants,salon-booking-system,5000,Salon booking system +edit_private_sln_bookings,salon-booking-system,5000,Salon booking system +edit_private_sln_services,salon-booking-system,5000,Salon booking system +edit_private_snippets,wp-snippets,1000,WP Snippets +edit_private_snitchs,snitch,1000,Snitch +edit_private_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +edit_private_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +edit_private_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +edit_private_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +edit_private_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +edit_private_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +edit_private_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +edit_private_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +edit_private_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_private_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +edit_private_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_private_stores,wp-store-locator,50000,WP Store Locator +edit_private_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_private_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_private_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_private_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_private_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_private_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_private_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_private_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_private_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_private_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +edit_private_tm-propertys,cherry-real-estate,600,Cherry Real Estate +edit_private_total_slider_slides,total-slider,500,Total Slider +edit_private_trans_logs,essential-real-estate,3000,Essential Real Estate +edit_private_translations,simple-punctual-translation,200,Simple Punctual Translation +edit_private_tribe_events,the-events-calendar,700000,The Events Calendar +edit_private_tribe_organizers,the-events-calendar,700000,The Events Calendar +edit_private_tribe_venues,the-events-calendar,700000,The Events Calendar +edit_private_user_packages,essential-real-estate,3000,Essential Real Estate +edit_private_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +edit_private_vacancies,job-board,300,Job Board by BestWebSoft +edit_private_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_private_wctrl_contents,widgets-control,1000,Widgets Control +edit_private_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +edit_private_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_private_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_private_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_private_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_private_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_private_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +edit_private_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_private_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_private_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_private_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_private_wpp_properties,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +edit_private_wpsdealss,deals-engine,200,Social Deals Engine +edit_private_wpsdealssaless,deals-engine,200,Social Deals Engine +edit_product,dc-woocommerce-multi-vendor,10000,WC Marketplace +edit_product,design-approval-system,500,Design Approval System +edit_product,easy-digital-downloads,60000,Easy Digital Downloads +edit_product,gnucommerce,1000,GNUCommerce +edit_product,jigoshop,4000,Jigoshop +edit_product,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_product,webmaster-user-role,8000,Webmaster User Role +edit_product,woocommerce,4000000,WooCommerce +edit_product,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_product,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +edit_product_categories,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +edit_product_categories,post-type-x,1000,Product Catalog X +edit_product_set,datafeedr-product-sets,1000,Datafeedr Product Sets +edit_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +edit_product_terms,easy-digital-downloads,60000,Easy Digital Downloads +edit_product_terms,jigoshop,4000,Jigoshop +edit_product_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_product_terms,webmaster-user-role,8000,Webmaster User Role +edit_product_terms,woocommerce,4000000,WooCommerce +edit_products,dc-woocommerce-multi-vendor,10000,WC Marketplace +edit_products,design-approval-system,500,Design Approval System +edit_products,easy-digital-downloads,60000,Easy Digital Downloads +edit_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +edit_products,gnucommerce,1000,GNUCommerce +edit_products,jigoshop,4000,Jigoshop +edit_products,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_products,post-type-x,1000,Product Catalog X +edit_products,products,300,WP Products +edit_products,webmaster-user-role,8000,Webmaster User Role +edit_products,woocommerce,4000000,WooCommerce +edit_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_products,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +edit_profile_cct,profile-custom-content-type,200,Profile CCT +edit_profiles_cct,profile-custom-content-type,200,Profile CCT +edit_project,upstream,1000,WordPress Project Management by UpStream +edit_project_author,upstream,1000,WordPress Project Management by UpStream +edit_project_terms,upstream,1000,WordPress Project Management by UpStream +edit_projects,projectmanager,400,ProjectManager +edit_projects,upstream,1000,WordPress Project Management by UpStream +edit_projects_settings,projectmanager,400,ProjectManager +edit_property,essential-real-estate,3000,Essential Real Estate +edit_property_terms,essential-real-estate,3000,Essential Real Estate +edit_propertys,essential-real-estate,3000,Essential Real Estate +edit_psp_project,project-panorama-lite,1000,Project Panorama +edit_psp_projects,project-panorama-lite,1000,Project Panorama +edit_published_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_published_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_published_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +edit_published_ads,apply-online,5000,Apply Online +edit_published_aec_events,another-events-calendar,800,Another Events Calendar +edit_published_aec_organizers,another-events-calendar,800,Another Events Calendar +edit_published_aec_venues,another-events-calendar,800,Another Events Calendar +edit_published_affiliate_keywords,affiliate,700,Affiliate +edit_published_agents,essential-real-estate,3000,Essential Real Estate +edit_published_aggregator-records,the-events-calendar,700000,The Events Calendar +edit_published_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +edit_published_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +edit_published_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +edit_published_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +edit_published_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +edit_published_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +edit_published_anbs,alert-notice-boxes,1000,Alert Notice Boxes +edit_published_applications,apply-online,5000,Apply Online +edit_published_archivs,archive,700,Archive +edit_published_articles,issuem,1000,IssueM +edit_published_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +edit_published_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +edit_published_awebookings,awebooking,6000,AweBooking – Hotel Booking System +edit_published_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_published_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_published_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_published_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_published_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_published_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +edit_published_blocks,gutenberg,500000,Gutenberg +edit_published_board_committees,nonprofit-board-management,400,Nonprofit Board Management +edit_published_board_events,nonprofit-board-management,400,Nonprofit Board Management +edit_published_book-reviews,book-review-library,700,Book Review Library +edit_published_books,novelist,800,Novelist +edit_published_bps_forms,bp-profile-search,10000,BP Profile Search +edit_published_calp_events,calpress-event-calendar,5000,CalPress Calendar +edit_published_campaigns,charitable,10000,Charitable – Donation Plugin +edit_published_campaigns,leyka,1000,Leyka +edit_published_car_listings,wp-car-manager,3000,WP Car Manager +edit_published_cctor_coupons,coupon-creator,10000,Coupon Creator +edit_published_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +edit_published_classified_listings,classifieds-wp,800,Classifieds WP +edit_published_clients,upstream,1000,WordPress Project Management by UpStream +edit_published_courses,lifterlms,8000,LifterLMS +edit_published_ctas,cta,10000,WordPress Calls to Action +edit_published_cupri_pays,pardakht-delkhah,1000,پلاگین پرداخت دلخواه +edit_published_custom_csss,custom-css-js,100000,Simple Custom CSS and JS +edit_published_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +edit_published_documents,wp-document-revisions,4000,WP Document Revisions +edit_published_donations,charitable,10000,Charitable – Donation Plugin +edit_published_donations,leyka,1000,Leyka +edit_published_edr_courses,educator,1000,Educator 2 +edit_published_edr_lessons,educator,1000,Educator 2 +edit_published_edr_memberships,educator,1000,Educator 2 +edit_published_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_published_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +edit_published_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_published_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_published_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +edit_published_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +edit_published_emd_quotes,request-a-quote,1000,Request a Quote +edit_published_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_published_epa_albums,easy-photo-album,5000,Easy Photo Album +edit_published_event_listings,wp-event-manager,1000,WP Event Manager +edit_published_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +edit_published_events,events-maker,4000,Events Maker by dFactory +edit_published_events,quick-event-manager,5000,Quick Event Manager +edit_published_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +edit_published_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +edit_published_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +edit_published_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +edit_published_fep_announcements,front-end-pm,8000,Front End PM +edit_published_fep_messages,front-end-pm,8000,Front End PM +edit_published_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +edit_published_food_groups,restaurantpress,3000,RestaurantPress +edit_published_food_menus,restaurantpress,3000,RestaurantPress +edit_published_forms,pronamic-ideal,6000,Pronamic Pay +edit_published_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_published_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +edit_published_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +edit_published_glossaries,glossary-by-codeat,1000,Glossary +edit_published_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +edit_published_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +edit_published_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_published_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +edit_published_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +edit_published_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +edit_published_ib_edu_memberships,ibeducator,1000,Educator +edit_published_ib_educator_courses,ibeducator,1000,Educator +edit_published_ib_educator_lessons,ibeducator,1000,Educator +edit_published_ims_gallerys,image-store,900,Image Store +edit_published_inbound-forms,cta,10000,WordPress Calls to Action +edit_published_inbound-forms,landing-pages,10000,WordPress Landing Pages +edit_published_inbound-forms,leads,7000,WordPress Leads +edit_published_insertcodes,insert-code,300,Insert Code +edit_published_invoices,essential-real-estate,3000,Essential Real Estate +edit_published_issues,issuem,1000,IssueM +edit_published_job_listings,wp-job-manager,100000,WP Job Manager +edit_published_jscp_matchs,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_published_jscp_players,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_published_jscp_teams,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +edit_published_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +edit_published_landing_pages,landing-pages,10000,WordPress Landing Pages +edit_published_leads,cta,10000,WordPress Calls to Action +edit_published_leads,landing-pages,10000,WordPress Landing Pages +edit_published_leads,leads,7000,WordPress Leads +edit_published_legalpack_pages,legalpack,400,Legalpack +edit_published_lessons,lifterlms,8000,LifterLMS +edit_published_listings,auto-listings,400,Auto Listings +edit_published_listings,wp-real-estate,400,WP Real Estate +edit_published_listings,wpcasa,2000,WPCasa +edit_published_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_published_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_published_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +edit_published_mbdb_book,mooberry-book-manager,1000,Mooberry Book Manager +edit_published_mbdb_book_grid,mooberry-book-manager,1000,Mooberry Book Manager +edit_published_mbdb_book_grids,mooberry-book-manager,1000,Mooberry Book Manager +edit_published_mbdb_books,mooberry-book-manager,1000,Mooberry Book Manager +edit_published_meals,restaurant-manager,800,Restaurant Manager +edit_published_memberships,lifterlms,8000,LifterLMS +edit_published_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_published_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +edit_published_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +edit_published_nemus-sliders,nemus-slider,2000,Nemus Slider +edit_published_news,news-manager,3000,News Manager +edit_published_opalestate_agentss,opal-estate,1000,Opal Estate +edit_published_opalestate_propertiess,opal-estate,1000,Opal Estate +edit_published_packages,essential-real-estate,3000,Essential Real Estate +edit_published_payments,pronamic-ideal,6000,Pronamic Pay +edit_published_players,team-rosters,800,Team Rosters +edit_published_playlists,radio-station,1000,Radio Station +edit_published_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +edit_published_portfolios,flash-toolkit,30000,Flash Toolkit +edit_published_portfolios,suffice-toolkit,5000,Suffice Toolkit +edit_published_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +edit_published_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +edit_published_products,design-approval-system,500,Design Approval System +edit_published_products,easy-digital-downloads,60000,Easy Digital Downloads +edit_published_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +edit_published_products,gnucommerce,1000,GNUCommerce +edit_published_products,jigoshop,4000,Jigoshop +edit_published_products,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_published_products,post-type-x,1000,Product Catalog X +edit_published_products,products,300,WP Products +edit_published_products,webmaster-user-role,8000,Webmaster User Role +edit_published_products,woocommerce,4000000,WooCommerce +edit_published_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_published_products,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +edit_published_projects,upstream,1000,WordPress Project Management by UpStream +edit_published_propertys,essential-real-estate,3000,Essential Real Estate +edit_published_psp_projects,project-panorama-lite,1000,Project Panorama +edit_published_questions,lifterlms,8000,LifterLMS +edit_published_quizzes,lifterlms,8000,LifterLMS +edit_published_quotes,mg-quotes,300,mg Quotes +edit_published_redirects,wp-redirects,700,WP Redirects +edit_published_rem_properties,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +edit_published_reservations,restaurant-manager,800,Restaurant Manager +edit_published_resume_positions,wp-resume,700,WP Resume +edit_published_room_reservations,wp-hotelier,1000,Easy WP Hotelier +edit_published_room_types,awebooking,6000,AweBooking – Hotel Booking System +edit_published_rooms,wp-hotelier,1000,Easy WP Hotelier +edit_published_rsvpmakers,rsvpmaker,1000,RSVPMaker +edit_published_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_published_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +edit_published_shop_coupons,jigoshop,4000,Jigoshop +edit_published_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_published_shop_coupons,webmaster-user-role,8000,Webmaster User Role +edit_published_shop_coupons,woocommerce,4000000,WooCommerce +edit_published_shop_coupons,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +edit_published_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +edit_published_shop_emails,jigoshop,4000,Jigoshop +edit_published_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_published_shop_orders,jigoshop,4000,Jigoshop +edit_published_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_published_shop_orders,webmaster-user-role,8000,Webmaster User Role +edit_published_shop_orders,woocommerce,4000000,WooCommerce +edit_published_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +edit_published_shop_webhooks,woocommerce,4000000,WooCommerce +edit_published_shows,radio-station,1000,Radio Station +edit_published_sln_attendants,salon-booking-system,5000,Salon booking system +edit_published_sln_bookings,salon-booking-system,5000,Salon booking system +edit_published_sln_services,salon-booking-system,5000,Salon booking system +edit_published_snippets,wp-snippets,1000,WP Snippets +edit_published_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +edit_published_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +edit_published_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +edit_published_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +edit_published_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +edit_published_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +edit_published_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +edit_published_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +edit_published_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_published_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +edit_published_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_published_stores,wp-store-locator,50000,WP Store Locator +edit_published_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_published_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_published_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_published_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_published_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_published_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_published_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_published_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_published_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_published_tm-propertys,cherry-real-estate,600,Cherry Real Estate +edit_published_total_slider_slides,total-slider,500,Total Slider +edit_published_trans_logs,essential-real-estate,3000,Essential Real Estate +edit_published_translations,simple-punctual-translation,200,Simple Punctual Translation +edit_published_tribe_events,the-events-calendar,700000,The Events Calendar +edit_published_tribe_organizers,the-events-calendar,700000,The Events Calendar +edit_published_tribe_venues,the-events-calendar,700000,The Events Calendar +edit_published_un_feedback,usernoise,7000,Usernoise modal feedback / contact form +edit_published_user_packages,essential-real-estate,3000,Essential Real Estate +edit_published_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +edit_published_vacancies,job-board,300,Job Board by BestWebSoft +edit_published_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_published_wctrl_contents,widgets-control,1000,Widgets Control +edit_published_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +edit_published_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_published_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_published_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_published_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_published_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_published_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +edit_published_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +edit_published_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_published_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_published_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_published_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_published_wppizzas,wppizza,2000,WPPizza +edit_published_wprm_reservations,wp-restaurant-manager,700,WP Restaurant Manager +edit_published_wpsdealss,deals-engine,200,Social Deals Engine +edit_published_wpsdealssaless,deals-engine,200,Social Deals Engine +edit_published_ycd_countdowns,countdown-builder,1000,Countdown +edit_question,lifterlms,8000,LifterLMS +edit_questions,lifterlms,8000,LifterLMS +edit_quiz,lifterlms,8000,LifterLMS +edit_quizzes,lifterlms,8000,LifterLMS +edit_quote_authors,mg-quotes,300,mg Quotes +edit_quote_categories,mg-quotes,300,mg Quotes +edit_quotes,mg-quotes,300,mg Quotes +edit_raq_services,request-a-quote,1000,Request a Quote +edit_recurring_events,events-manager,100000,Events Manager +edit_redirects,wp-redirects,700,WP Redirects +edit_rem_properties,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +edit_rem_property,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +edit_replies,bbpress,300000,bbPress +edit_reservations,restaurant-manager,800,Restaurant Manager +edit_restaurant_items,restaurant,300,Restaurant +edit_resume,wp-resume,700,WP Resume +edit_resume_organizations,wp-resume,700,WP Resume +edit_resume_positions,wp-resume,700,WP Resume +edit_resume_sections,wp-resume,700,WP Resume +edit_role_menus,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_roles,members,100000,Members +edit_roles,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_room,wp-hotelier,1000,Easy WP Hotelier +edit_room_reservation,wp-hotelier,1000,Easy WP Hotelier +edit_room_reservation_terms,wp-hotelier,1000,Easy WP Hotelier +edit_room_reservations,wp-hotelier,1000,Easy WP Hotelier +edit_room_terms,wp-hotelier,1000,Easy WP Hotelier +edit_room_type,awebooking,6000,AweBooking – Hotel Booking System +edit_room_type_terms,awebooking,6000,AweBooking – Hotel Booking System +edit_room_types,awebooking,6000,AweBooking – Hotel Booking System +edit_rooms,wp-hotelier,1000,Easy WP Hotelier +edit_rsvpemail,rsvpmaker,1000,RSVPMaker +edit_rsvpemails,rsvpmaker,1000,RSVPMaker +edit_rsvpmakers,rsvpmaker,1000,RSVPMaker +edit_schedule,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_seasons,leaguemanager,2000,LeagueManager +edit_sgpb_popup,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +edit_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +edit_shift,employee-scheduler,400,Shiftee Basic – Employee and Staff Scheduling +edit_shifts,employee-scheduler,400,Shiftee Basic – Employee and Staff Scheduling +edit_shop_coupon,jigoshop,4000,Jigoshop +edit_shop_coupon,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_coupon,webmaster-user-role,8000,Webmaster User Role +edit_shop_coupon,woocommerce,4000000,WooCommerce +edit_shop_coupon_terms,jigoshop,4000,Jigoshop +edit_shop_coupon_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_coupon_terms,webmaster-user-role,8000,Webmaster User Role +edit_shop_coupon_terms,woocommerce,4000000,WooCommerce +edit_shop_coupons,jigoshop,4000,Jigoshop +edit_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_coupons,webmaster-user-role,8000,Webmaster User Role +edit_shop_coupons,woocommerce,4000000,WooCommerce +edit_shop_coupons,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +edit_shop_discount,easy-digital-downloads,60000,Easy Digital Downloads +edit_shop_discount_terms,easy-digital-downloads,60000,Easy Digital Downloads +edit_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +edit_shop_email,jigoshop,4000,Jigoshop +edit_shop_email,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_email_terms,jigoshop,4000,Jigoshop +edit_shop_email_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_emails,jigoshop,4000,Jigoshop +edit_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_order,jigoshop,4000,Jigoshop +edit_shop_order,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_order,webmaster-user-role,8000,Webmaster User Role +edit_shop_order,woocommerce,4000000,WooCommerce +edit_shop_order_terms,jigoshop,4000,Jigoshop +edit_shop_order_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_order_terms,webmaster-user-role,8000,Webmaster User Role +edit_shop_order_terms,woocommerce,4000000,WooCommerce +edit_shop_orders,jigoshop,4000,Jigoshop +edit_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +edit_shop_orders,webmaster-user-role,8000,Webmaster User Role +edit_shop_orders,woocommerce,4000000,WooCommerce +edit_shop_payment,easy-digital-downloads,60000,Easy Digital Downloads +edit_shop_payment_terms,easy-digital-downloads,60000,Easy Digital Downloads +edit_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +edit_shop_webhook,woocommerce,4000000,WooCommerce +edit_shop_webhook_terms,woocommerce,4000000,WooCommerce +edit_shop_webhooks,woocommerce,4000000,WooCommerce +edit_shows,radio-station,1000,Radio Station +edit_sln_attendant,salon-booking-system,5000,Salon booking system +edit_sln_attendants,salon-booking-system,5000,Salon booking system +edit_sln_booking,salon-booking-system,5000,Salon booking system +edit_sln_bookings,salon-booking-system,5000,Salon booking system +edit_sln_service,salon-booking-system,5000,Salon booking system +edit_sln_services,salon-booking-system,5000,Salon booking system +edit_snippets,wp-snippets,1000,WP Snippets +edit_snitch,snitch,1000,Snitch +edit_snitchs,snitch,1000,Snitch +edit_sola_st_ticket,sola-support-tickets,400,Sola Support Tickets +edit_sola_st_tickets,sola-support-tickets,400,Sola Support Tickets +edit_sp_calendar,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_calendar_terms,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_config,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_config_terms,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_event,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_event_terms,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_list,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_list_terms,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_player,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_player_terms,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_staff,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_staff_terms,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_table,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_table_terms,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_team,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_team_terms,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +edit_sport,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_sprout_invoices,sprout-invoices,2000,Client Invoicing by Sprout Invoices – Easy Estimates and Invoices for WordPress +edit_stafflist,stafflist,200,StaffList +edit_stats_wd_ads_adverts,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +edit_stm_lms_post,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +edit_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +edit_store,wp-store-locator,50000,WP Store Locator +edit_store_order,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +edit_stores,wp-store-locator,50000,WP Store Locator +edit_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_sunshine_gallery,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_sunshine_order,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_sunshine_product,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +edit_support_ticket,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_support_ticket_comments,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +edit_tc_event,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_tc_order,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_tc_ticket,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_tc_tickets_instance,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +edit_team,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_teams,leaguemanager,2000,LeagueManager +edit_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +edit_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +edit_ticket_priority,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_ticket_status,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_ticket_topic,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +edit_tm-property_feature,cherry-real-estate,600,Cherry Real Estate +edit_tm-property_tag,cherry-real-estate,600,Cherry Real Estate +edit_tm-property_type,cherry-real-estate,600,Cherry Real Estate +edit_tm-propertys,cherry-real-estate,600,Cherry Real Estate +edit_topic_tags,bbpress,300000,bbPress +edit_topics,bbpress,300000,bbPress +edit_total_slider_slides,total-slider,500,Total Slider +edit_trans_log,essential-real-estate,3000,Essential Real Estate +edit_trans_log_terms,essential-real-estate,3000,Essential Real Estate +edit_trans_logs,essential-real-estate,3000,Essential Real Estate +edit_translation,simple-punctual-translation,200,Simple Punctual Translation +edit_translations,simple-punctual-translation,200,Simple Punctual Translation +edit_tribe_events,the-events-calendar,700000,The Events Calendar +edit_tribe_organizers,the-events-calendar,700000,The Events Calendar +edit_tribe_venues,the-events-calendar,700000,The Events Calendar +edit_un_feedback,usernoise,7000,Usernoise modal feedback / contact form +edit_un_feedback_items,usernoise,7000,Usernoise modal feedback / contact form +edit_user_package,essential-real-estate,3000,Essential Real Estate +edit_user_package_terms,essential-real-estate,3000,Essential Real Estate +edit_user_packages,essential-real-estate,3000,Essential Real Estate +edit_user_registration,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +edit_user_registration_terms,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +edit_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +edit_usergroups,edit-flow,10000,Edit Flow +edit_usergroups,publishpress,1000,PublishPress – Professional publishing tools for WordPress +edit_users_higher_level,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_vacancies,job-board,300,Job Board by BestWebSoft +edit_vacancy,job-board,300,Job Board by BestWebSoft +edit_venue,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +edit_wbcr-snippets,insert-php,100000,PHP code snippets (Insert PHP) +edit_wbcr-snippetss,insert-php,100000,PHP code snippets (Insert PHP) +edit_wctrl_contents,widgets-control,1000,Widgets Control +edit_wd_ads_advert,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +edit_wd_ads_adverts,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +edit_wd_ads_groups,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +edit_wd_ads_schedule,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +edit_wd_ads_schedules,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +edit_whistles,whistles,2000,Whistles +edit_widget_permissions,wpfront-user-role-editor,60000,WPFront User Role Editor +edit_wiki,wordpress-wiki,400,WordPress Wiki +edit_wiki_page,wordpress-wiki,400,WordPress Wiki +edit_wiki_pages,wordpress-wiki,400,WordPress Wiki +edit_wordlift_entities,wordlift,400,WordLift – AI powered SEO +edit_wordlift_entity,wordlift,400,WordLift – AI powered SEO +edit_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +edit_wpcm_club,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_club_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_match,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_match_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_player,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_player_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_sponsor,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_sponsor_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_staff,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_staff_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +edit_wpdiscuz_form,wpdiscuz,40000,Comments – wpDiscuz +edit_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +edit_wpfc_sermon,sermon-manager-for-wordpress,9000,Sermon Manager +edit_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +edit_wpi_discount,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_wpi_invoice,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_wpi_item,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_wpi_quote,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +edit_wplc_quick_response,wp-live-chat-support,60000,WP Live Chat Support +edit_wpp_properties,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +edit_wpp_property,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +edit_wppizza,wppizza,2000,WPPizza +edit_wppizzas,wppizza,2000,WPPizza +edit_wprm_reservation,wp-restaurant-manager,700,WP Restaurant Manager +edit_wprm_reservations,wp-restaurant-manager,700,WP Restaurant Manager +edit_wpsdeals,deals-engine,200,Social Deals Engine +edit_wpsdeals_terms,deals-engine,200,Social Deals Engine +edit_wpsdealss,deals-engine,200,Social Deals Engine +edit_wpsdealssales,deals-engine,200,Social Deals Engine +edit_wpsdealssales_terms,deals-engine,200,Social Deals Engine +edit_wpsdealssaless,deals-engine,200,Social Deals Engine +edit_wpse_profiles,wp-smart-editor,900,WP Smart Editor +edit_wswebinar,wp-webinarsystem,2000,WP WebinarSystem +edit_wswebinars,wp-webinarsystem,2000,WP WebinarSystem +edit_ycd_countdown,countdown-builder,1000,Countdown +edit_ycd_countdowns,countdown-builder,1000,Countdown +edit_yop_polls,yop-poll,20000,YOP Poll +edit_yop_polls_templates,yop-poll,20000,YOP Poll +editor,webmaster-user-role,8000,Webmaster User Role +editore_atti_albo,albo-pretorio-on-line,2000,Albo Pretorio On line +edr_edit_quiz_grades_all,educator,1000,Educator 2 +edr_edit_quiz_grades_own,educator,1000,Educator 2 +educator_edit_entries,educator,1000,Educator 2 +educator_edit_entries,ibeducator,1000,Educator +ee_assign_event_category,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_assign_event_type,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_assign_venue_category,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_checkin,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_checkins,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_contact,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_contacts,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_default_price,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_default_price_type,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_default_price_types,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_default_prices,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_default_ticket,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_default_tickets,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_event,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_event_category,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_event_type,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_global_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_message,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_others_checkins,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_others_default_tickets,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_others_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_others_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_others_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_payment_method,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_payment_methods,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_payments,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_private_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_private_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_published_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_published_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_question,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_question_group,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_question_groups,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_questions,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_registration,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_registrations,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_venue,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_venue_category,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_delete_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_checkin,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_checkins,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_contact,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_contacts,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_default_price,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_default_price_type,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_default_price_types,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_default_prices,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_default_ticket,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_default_tickets,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_event,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_event_category,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_event_type,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_global_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_message,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_others_checkins,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_others_default_tickets,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_others_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_others_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_others_payment_methods,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_others_registrations,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_others_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_payment_method,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_payment_methods,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_payments,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_private_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_private_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_published_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_published_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_question,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_question_group,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_question_groups,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_questions,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_registration,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_registrations,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_system_question_groups,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_system_questions,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_venue,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_venue_category,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_edit_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_manage_event_categories,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_manage_event_types,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_manage_gateways,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_manage_venue_categories,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_payment_method_admin_only,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_payment_method_bank,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_payment_method_check,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_payment_method_invoice,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_payment_method_paypal_express,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_payment_method_paypal_standard,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_publish_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_publish_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_checkin,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_checkins,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_contact,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_contacts,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_default_price_types,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_default_prices,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_default_ticket,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_default_tickets,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_ee,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_event,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_global_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_message,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_others_checkins,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_others_default_tickets,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_others_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_others_messages,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_others_payment_methods,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_others_registrations,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_others_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_payment_method,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_payment_methods,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_private_events,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_private_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_question_groups,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_questions,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_registration,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_registrations,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_transaction,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_transactions,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_venue,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_read_venues,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ee_send_message,event-espresso-decaf,3000,Event Espresso 4 Decaf – Event Registration Event Ticketing +ef_view_calendar,edit-flow,10000,Edit Flow +ef_view_story_budget,edit-flow,10000,Edit Flow +email_multiple_users,email-users,10000,Email Users +email_single_user,email-users,10000,Email Users +email_user_groups,email-users,10000,Email Users +email_users_notify,email-users,10000,Email Users +emu2_email_multiple_users,emu2-email-users-2,500,Emu2 – Email Users 2 +emu2_email_single_user,emu2-email-users-2,500,Emu2 – Email Users 2 +emu2_email_user_groups,emu2-email-users-2,500,Emu2 – Email Users 2 +emu2_email_users_notify,emu2-email-users-2,500,Emu2 – Email Users 2 +emu2_export_list,emu2-email-users-2,500,Emu2 – Email Users 2 +emu2_manage_options,emu2-email-users-2,500,Emu2 – Email Users 2 +enable_cometchat,cometchat,600,"Voice, Video & Text Chat by CometChat – Best WordPress Chat Plugin" +encode_videos,video-embed-thumbnail-generator,30000,Video Embed & Thumbnail Generator +enroll,lifterlms,8000,LifterLMS +erp_ac_create_account,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_create_bank_transfer,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_create_customer,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_create_expenses_credit,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_create_expenses_voucher,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_create_journal,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_create_sales_invoice,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_create_sales_payment,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_create_vendor,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_delete_account,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_delete_customer,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_delete_other_customers,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_delete_other_vendors,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_delete_vendor,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_edit_account,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_edit_customer,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_edit_other_customers,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_edit_other_vendors,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_edit_vendor,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_publish_expenses_credit,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_publish_expenses_voucher,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_publish_sales_invoice,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_publish_sales_payment,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_account_lists,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_bank_accounts,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_customer,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_dashboard,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_expense,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_expenses_summary,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_journal,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_other_customers,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_other_expenses,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_other_journals,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_other_sales,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_other_vendors,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_reports,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_sale,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_sales_summary,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_single_account,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_single_customer,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_single_vendor,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_ac_view_vendor,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_can_terminate,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crate_announcement,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_create_attendance,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_create_dependent,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_create_document,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_create_education,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_create_employee,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_create_experience,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_create_review,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_add_contact,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_create_groups,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_delete_contact,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_delete_groups,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_edit_contact,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_edit_groups,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_list_contact,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_manage_activites,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_manage_dashboard,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_manage_groups,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_crm_manage_schedules,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_delete_attendance,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_delete_dependent,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_delete_document,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_delete_education,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_delete_employee,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_delete_experience,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_delete_review,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_edit_attendance,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_edit_dependent,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_edit_document,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_edit_education,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_edit_employee,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_edit_employees,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_edit_experience,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_leave_create_request,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_leave_manage,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_list_employee,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_manage_announcement,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_manage_department,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_manage_designation,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_manage_hr_settings,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_manage_jobinfo,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_manage_review,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_announcement,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_attendance,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_dependent,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_document,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_education,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_employee,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_experience,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_jobinfo,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +erp_view_list,erp,7000,"WP ERP – HRM, CRM & Accounting Solution For WordPress" +execute_php,php-execution-plugin,9000,PHP Execution +export_admincolorschemes,easy-admin-color-schemes,1000,Easy Admin Color Schemes +export_charitable_reports,charitable,10000,Charitable – Donation Plugin +export_give_reports,give,50000,Give – Donation Plugin and Fundraising Platform +export_leagues,leaguemanager,2000,LeagueManager +export_masterslider,master-slider,100000,Master Slider – Responsive Touch Slider +export_opalestate_reports,opal-estate,1000,Opal Estate +export_others_admincolorschemes,easy-admin-color-schemes,1000,Easy Admin Color Schemes +export_shop_reports,easy-digital-downloads,60000,Easy Digital Downloads +fbm_edit_background,fully-background-manager,10000,Full Background Manager +fluentform_dashboard_access,fluentform,400,WP Fluent Form – WordPress Contact Form Plugin with Advanced Form Builder Features +fluentform_entries_viewer,fluentform,400,WP Fluent Form – WordPress Contact Form Plugin with Advanced Form Builder Features +fluentform_forms_manager,fluentform,400,WP Fluent Form – WordPress Contact Form Plugin with Advanced Form Builder Features +fluentform_full_access,fluentform,400,WP Fluent Form – WordPress Contact Form Plugin with Advanced Form Builder Features +fluentform_settings_manager,fluentform,400,WP Fluent Form – WordPress Contact Form Plugin with Advanced Form Builder Features +flush_cache_all,nginx-champuru,2000,Nginx Cache Controller +flush_cache_single,nginx-champuru,2000,Nginx Cache Controller +frm_change_settings,formidable,200000,Formidable Forms – Form Builder for WordPress +frm_delete_entries,formidable,200000,Formidable Forms – Form Builder for WordPress +frm_delete_forms,formidable,200000,Formidable Forms – Form Builder for WordPress +frm_edit_forms,formidable,200000,Formidable Forms – Form Builder for WordPress +frm_view_entries,formidable,200000,Formidable Forms – Form Builder for WordPress +frm_view_entries,wp-essentials,200,WP Essentials +frm_view_forms,formidable,200000,Formidable Forms – Form Builder for WordPress +frm_view_forms,wp-essentials,200,WP Essentials +frm_view_reports,wp-essentials,200,WP Essentials +frontier_post_can_add,frontier-post,2000,Frontier Post +frontier_post_can_delete,frontier-post,2000,Frontier Post +frontier_post_can_draft,frontier-post,2000,Frontier Post +frontier_post_can_edit,frontier-post,2000,Frontier Post +frontier_post_can_media,frontier-post,2000,Frontier Post +frontier_post_can_page,frontier-post,2000,Frontier Post +frontier_post_can_pending,frontier-post,2000,Frontier Post +frontier_post_can_private,frontier-post,2000,Frontier Post +frontier_post_can_publish,frontier-post,2000,Frontier Post +frontier_post_exerpt_edit,frontier-post,2000,Frontier Post +frontier_post_redir_edit,frontier-post,2000,Frontier Post +frontier_post_show_admin_bar,frontier-post,2000,Frontier Post +frontier_post_tags_edit,frontier-post,2000,Frontier Post +gdcpttools_basic,gd-taxonomies-tools,3000,GD Custom Posts And Taxonomies Tools +gdoc_query_sql_databases,inline-google-spreadsheet-viewer,10000,Inline Google Spreadsheet Viewer +gdrts_standard,gd-rating-system,5000,GD Rating System +gest_atti_albo,albo-pretorio-on-line,2000,Albo Pretorio On line +gf_limit,gravity-forms-quantity-limits,700,Gravity Forms Quantity Limiter +gf_limit_uninstall,gravity-forms-quantity-limits,700,Gravity Forms Quantity Limiter +gmedia_album_manage,grand-media,20000,Gmedia Photo Gallery +gmedia_category_manage,grand-media,20000,Gmedia Photo Gallery +gmedia_delete_media,grand-media,20000,Gmedia Photo Gallery +gmedia_delete_others_media,grand-media,20000,Gmedia Photo Gallery +gmedia_edit_media,grand-media,20000,Gmedia Photo Gallery +gmedia_edit_others_media,grand-media,20000,Gmedia Photo Gallery +gmedia_gallery_manage,grand-media,20000,Gmedia Photo Gallery +gmedia_import,grand-media,20000,Gmedia Photo Gallery +gmedia_library,grand-media,20000,Gmedia Photo Gallery +gmedia_module_manage,grand-media,20000,Gmedia Photo Gallery +gmedia_settings,grand-media,20000,Gmedia Photo Gallery +gmedia_show_others_media,grand-media,20000,Gmedia Photo Gallery +gmedia_tag_manage,grand-media,20000,Gmedia Photo Gallery +gmedia_terms,grand-media,20000,Gmedia Photo Gallery +gmedia_terms_delete,grand-media,20000,Gmedia Photo Gallery +gmedia_upload,grand-media,20000,Gmedia Photo Gallery +gravityforms_directory,gravity-forms-addons,5000,Gravity Forms Directory +gravityforms_directory_uninstall,gravity-forms-addons,5000,Gravity Forms Directory +gravityforms_edit_forms,webmaster-user-role,8000,Webmaster User Role +gravityforms_exacttarget,gravity-forms-exacttarget,300,Gravity Forms ExactTarget Add-on +gravityforms_exacttarget_uninstall,gravity-forms-exacttarget,300,Gravity Forms ExactTarget Add-on +gravityforms_icontact,gravity-forms-icontact,600,iContact for Gravity Forms +gravityforms_icontact_uninstall,gravity-forms-icontact,600,iContact for Gravity Forms +gravityforms_infusionsoft,infusionsoft,6000,Infusionsoft Gravity Forms Add-on +gravityforms_infusionsoft_uninstall,infusionsoft,6000,Infusionsoft Gravity Forms Add-on +gravityforms_marketo,marketo,300,Marketo Gravity Forms Add-on +gravityforms_marketo_uninstall,marketo,300,Marketo Gravity Forms Add-on +gravityforms_remove,gravity-forms-remove-entries,900,Gravity Forms Remove Entries +gravityforms_remove_uninstall,gravity-forms-remove-entries,900,Gravity Forms Remove Entries +gravityforms_sendinblue,gravity-forms-sendinblue-add-on,800,Gravity Forms SendinBlue Add-On +gravityforms_sendinblue_uninstall,gravity-forms-sendinblue-add-on,800,Gravity Forms SendinBlue Add-On +gravityforms_shootq,gravity-forms-shootq-add-on,200,Gravity Forms ShootQ add-on +gravityforms_shootq_uninstall,gravity-forms-shootq-add-on,200,Gravity Forms ShootQ add-on +gravityforms_tave,gravity-forms-tave-add-on,200,Gravity Forms Táve add-on +gravityforms_tave_uninstall,gravity-forms-tave-add-on,200,Gravity Forms Táve add-on +gravityforms_view_entries,webmaster-user-role,8000,Webmaster User Role +groups_access,groups,20000,Groups +groups_admin_groups,groups,20000,Groups +groups_admin_options,groups,20000,Groups +groups_restrict_access,groups,20000,Groups +happyforms_manage_form,happyforms,4000,Contact Form to Manage and respond to conversations with customers — HappyForms +happyforms_manage_response,happyforms,4000,Contact Form to Manage and respond to conversations with customers — HappyForms +has_wallets,wallets,600,Bitcoin and Altcoin Wallets +haveown_snap_accss,social-networks-auto-poster-facebook-twitter-g,100000,NextScripts: Social Networks Auto-Poster +hclc_admin,locatoraid,1000,Locatoraid – Store Locator Plugin +help_yop_poll_page,yop-poll,20000,YOP Poll +hide_from_intercom,intercom-for-wordpress,400,Intercom for WordPress +hrm_employee,hrm,200,WP Human Resource Management +ihep_effects_settings,wp-overlays,2000,WP Overlays +ihep_how_overview,wp-overlays,2000,WP Overlays +ihep_manage_settings,wp-overlays,2000,WP Overlays +import_admincolorschemes,easy-admin-color-schemes,1000,Easy Admin Color Schemes +import_datasets,projectmanager,400,ProjectManager +import_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +import_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +import_leagues,leaguemanager,2000,LeagueManager +import_products,easy-digital-downloads,60000,Easy Digital Downloads +import_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +import_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +import_wp_polls,yop-poll,20000,YOP Poll +ims_add_galleries,image-store,900,Image Store +ims_change_permissions,image-store,900,Image Store +ims_change_pricing,image-store,900,Image Store +ims_change_settings,image-store,900,Image Store +ims_manage_customers,image-store,900,Image Store +ims_manage_galleries,image-store,900,Image Store +ims_read_galleries,image-store,900,Image Store +ims_read_sales,image-store,900,Image Store +install_modules,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +install_recommended_h5p_libraries,h5p,10000,Interactive Content – H5P +install_wordpoints_extensions,wordpoints,700,WordPoints +install_wordpoints_modules,wordpoints,700,WordPoints +invoke_force_refresh,force-refresh,600,Force Refresh +join_board_committee,nonprofit-board-management,400,Nonprofit Board Management +jsjobs,js-jobs,1000,JS Job Manager +jsp_matchday_edit,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +jsp_matchday_manage,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +jsst_support_ticket,js-support-ticket,3000,JS Support Ticket +keep_gate,bbpress,300000,bbPress +launch_editor,digiwidgets-image-editor,1000,DigiWidgets Image Editor +lazyest_author,lazyest-gallery,1000,Lazyest Gallery +lazyest_editor,lazyest-gallery,1000,Lazyest Gallery +lazyest_manager,lazyest-gallery,1000,Lazyest Gallery +league_manager,leaguemanager,2000,LeagueManager +leaguemanager,leaguemanager,2000,LeagueManager +leaguemanager_settings,leaguemanager,2000,LeagueManager +leenkme_edit_user_settings,leenkme,600,leenk.me +leenkme_manage_all_settings,leenkme,600,leenk.me +lenslider_manage,len-slider,1000,Len Slider +leyka_manage_donations,leyka,1000,Leyka +leyka_manage_options,leyka,1000,Leyka +lfb_manager,lead-form-builder,10000,Contact Form & Lead Form Builder +lifterlms_instructor,lifterlms,8000,LifterLMS +limitby_author_backend_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +limitby_author_frontend_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +limitby_tickets_assigned_to,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +list_hanaboard-post,hana-board,1000,Hana-Board 하나보드 워드프레스 게시판 +list_roles,members,100000,Members +list_roles,wpfront-user-role-editor,60000,WPFront User Role Editor +list_wallet_transactions,wallets,600,Bitcoin and Altcoin Wallets +ljmm_control,lj-maintenance-mode,50000,Maintenance Mode +ljmm_view_site,lj-maintenance-mode,50000,Maintenance Mode +loco_admin,loco-translate,700000,Loco Translate +log_cbxaccounting,cbxwpsimpleaccounting,300,CBX Accounting +mailpoet_access_plugin_admin,mailpoet,70000,MailPoet – emails and newsletters in WordPress +mailpoet_manage_emails,mailpoet,70000,MailPoet – emails and newsletters in WordPress +mailpoet_manage_forms,mailpoet,70000,MailPoet – emails and newsletters in WordPress +mailpoet_manage_segments,mailpoet,70000,MailPoet – emails and newsletters in WordPress +mailpoet_manage_settings,mailpoet,70000,MailPoet – emails and newsletters in WordPress +mailpoet_manage_subscribers,mailpoet,70000,MailPoet – emails and newsletters in WordPress +make_video_thumbnails,video-embed-thumbnail-generator,30000,Video Embed & Thumbnail Generator +manage DB views,dbview,400,dbview +manage-post-highlights,post-highlights,200,post highlights +manage-wp-users-exporter,wp-users-exporter,2000,WP Users Exporter +manage_acadp_options,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +manage_achievement_events,achievements,300,Achievements for WordPress +manage_ad_terms,apply-online,5000,Apply Online +manage_admin_columns,codepress-admin-columns,100000,Admin Columns +manage_admincolorschemes_settings,easy-admin-color-schemes,1000,Easy Admin Color Schemes +manage_ads,pixel-caffeine,30000,Pixel Caffeine +manage_aec_options,another-events-calendar,800,Another Events Calendar +manage_agent_terms,essential-real-estate,3000,Essential Real Estate +manage_ai1ec_feeds,all-in-one-event-calendar,100000,All-in-One Event Calendar +manage_ai1ec_options,all-in-one-event-calendar,100000,All-in-One Event Calendar +manage_aiovg_options,all-in-one-video-gallery,1000,All-in-One Video Gallery +manage_amazon_listings,wp-lister-for-amazon,2000,WP-Lister Lite for Amazon +manage_amazon_options,wp-lister-for-amazon,2000,WP-Lister Lite for Amazon +manage_archive_structure,archive,700,Archive +manage_article_categories,issuem,1000,IssueM +manage_article_tags,issuem,1000,IssueM +manage_atbdp_options,directorist,500,Directorist – Business Directory Plugin +manage_attendance,hrm,200,WP Human Resource Management +manage_attendees_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +manage_awebooking,awebooking,6000,AweBooking – Hotel Booking System +manage_awebooking_settings,awebooking,6000,AweBooking – Hotel Booking System +manage_awebooking_terms,awebooking,6000,AweBooking – Hotel Booking System +manage_birs_settings,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +manage_book_review_options,book-review-library,700,Book Review Library +manage_book_terms,novelist,800,Novelist +manage_bookings,events-manager,100000,Events Manager +manage_bookings,restaurant-reservations,20000,Restaurant Reservations +manage_campaign_terms,charitable,10000,Charitable – Donation Plugin +manage_cannedresponse_category,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +manage_cannedresponse_tag,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +manage_capabilities,capability-manager-enhanced,70000,Capability Manager Enhanced +manage_capabilities,capsman,10000,Capability Manager +manage_car_listing_terms,wp-car-manager,3000,WP Car Manager +manage_car_listings,wp-car-manager,3000,WP Car Manager +manage_cbxaccounting,cbxwpsimpleaccounting,300,CBX Accounting +manage_cforms,cforms2,10000,cformsII +manage_charitable_settings,charitable,10000,Charitable – Donation Plugin +manage_cimy_image_rotator,cimy-header-image-rotator,4000,Cimy Header Image Rotator +manage_circulation,weblibrarian,700,WebLibrarian +manage_classified_listing_terms,classifieds-wp,800,Classifieds WP +manage_classified_listings,classifieds-wp,800,Classifieds WP +manage_classifieds,another-wordpress-classifieds-plugin,10000,AWPCP – Classifieds Plugin +manage_classifieds_listings,another-wordpress-classifieds-plugin,10000,AWPCP – Classifieds Plugin +manage_client_terms,upstream,1000,WordPress Project Management by UpStream +manage_collection,weblibrarian,700,WebLibrarian +manage_contact_country,wp-easy-contact,600,Best Contact Management Software for WordPress +manage_contact_manager,contact-manager,500,Contact Manager +manage_contact_state,wp-easy-contact,600,Best Contact Management Software for WordPress +manage_contact_tag,wp-easy-contact,600,Best Contact Management Software for WordPress +manage_contact_topic,wp-easy-contact,600,Best Contact Management Software for WordPress +manage_content_types,cms-press,1000,CMS Press +manage_contests,and-the-winner-is,300,And The Winner Is… +manage_course_cats,lifterlms,8000,LifterLMS +manage_course_difficulties,lifterlms,8000,LifterLMS +manage_course_tags,lifterlms,8000,LifterLMS +manage_course_tracks,lifterlms,8000,LifterLMS +manage_cover_artist_terms,mooberry-book-manager,1000,Mooberry Book Manager +manage_crm,wp-smart-crm-invoices-free,300,WP smart CRM & Invoices FREE +manage_customfields,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +manage_database,wp-dbmanager,100000,WP-DBManager +manage_department,hrm,200,WP Human Resource Management +manage_departments,employee-directory,400,Staff Directory – Employee Directory for WordPress +manage_designation,hrm,200,WP Human Resource Management +manage_discount_codes_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +manage_ditty_news_ticker_settings,ditty-news-ticker,40000,Ditty News Ticker +manage_download_attachments,download-attachments,10000,Download Attachments +manage_downloads,download-monitor,100000,Download Monitor +manage_downloads,hacklog-downloadmanager,300,Hacklog DownloadManager +manage_downloads,wp-downloadmanager,8000,WP-DownloadManager +manage_ebay_listings,wp-lister-for-ebay,5000,WP-Lister Lite for eBay +manage_ebay_options,wp-lister-for-ebay,5000,WP-Lister Lite for eBay +manage_editor_terms,mooberry-book-manager,1000,Mooberry Book Manager +manage_editorial_access,editorial-access-manager,400,Editorial Access Manager +manage_educator,educator,1000,Educator 2 +manage_educator,ibeducator,1000,Educator +manage_email,wp-email,10000,WP-EMail +manage_email_categories,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +manage_email_logs,email-log,20000,Email Log +manage_email_settings,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +manage_employee,hrm,200,WP Human Resource Management +manage_employee_profile,hrm,200,WP Human Resource Management +manage_employee_tags,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +manage_employment_type,employee-directory,400,Staff Directory – Employee Directory for WordPress +manage_event_categories,event-organiser,40000,Event Organiser +manage_event_categories,events-maker,4000,Events Maker by dFactory +manage_event_listing_terms,wp-event-manager,1000,WP Event Manager +manage_event_listings,wp-event-manager,1000,WP Event Manager +manage_event_locations,events-maker,4000,Events Maker by dFactory +manage_event_magic_terms,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +manage_event_organizers,events-maker,4000,Events Maker by dFactory +manage_event_tags,events-maker,4000,Events Maker by dFactory +manage_events_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +manage_events_categories,all-in-one-event-calendar,100000,All-in-One Event Calendar +manage_everest_form_terms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +manage_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +manage_ezemails,ez-emails,500,EZ Emails +manage_fa_terms,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +manage_facebook_awd_opengraph,facebook-awd,1000,Facebook AWD All in one +manage_facebook_awd_plugins,facebook-awd,1000,Facebook AWD All in one +manage_facebook_awd_publish_to_pages,facebook-awd,1000,Facebook AWD All in one +manage_facebook_awd_settings,facebook-awd,1000,Facebook AWD All in one +manage_feed_settings,wp-rss-aggregator,60000,WP RSS Aggregator +manage_feed_source_terms,wp-rss-aggregator,60000,WP RSS Aggregator +manage_feed_terms,wp-rss-aggregator,60000,WP RSS Aggregator +manage_filter_groups,plugin-organizer,10000,Plugin Organizer +manage_flash_toolkit,flash-toolkit,30000,Flash Toolkit +manage_fonts,font-organizer,20000,Font Organizer +manage_food_group_terms,restaurantpress,3000,RestaurantPress +manage_food_menu_terms,restaurantpress,3000,RestaurantPress +manage_football_pool,football-pool,1000,Football Pool +manage_forms,formlift,800,FormLift for Infusionsoft Web Forms +manage_gender,employee-directory,400,Staff Directory – Employee Directory for WordPress +manage_genre_terms,mooberry-book-manager,1000,Mooberry Book Manager +manage_give_form_terms,give,50000,Give – Donation Plugin and Fundraising Platform +manage_give_payment_terms,give,50000,Give – Donation Plugin and Fundraising Platform +manage_give_settings,give,50000,Give – Donation Plugin and Fundraising Platform +manage_glossaries,glossary-by-codeat,1000,Glossary +manage_groups,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +manage_guiform,guiform,400,GuiForm +manage_h5p_libraries,h5p,10000,Interactive Content – H5P +manage_hami,wp2appir,300,wp2appir +manage_hb_booking,wp-hotel-booking,7000,WP Hotel Booking +manage_hotel_location_terms,awebooking,6000,AweBooking – Hotel Booking System +manage_hotel_service_terms,awebooking,6000,AweBooking – Hotel Booking System +manage_hotelier,wp-hotelier,1000,Easy WP Hotelier +manage_hrm_organization,hrm,200,WP Human Resource Management +manage_illustrator_terms,mooberry-book-manager,1000,Mooberry Book Manager +manage_invoice_terms,essential-real-estate,3000,Essential Real Estate +manage_invoicing,invoicing,2000,Invoicing – Invoice & Payments Plugin +manage_issuem_settings,issuem,1000,IssueM +manage_issues,issuem,1000,IssueM +manage_jbbrd_businesses_tags,job-board,300,Job Board by BestWebSoft +manage_jbbrd_employment_tags,job-board,300,Job Board by BestWebSoft +manage_jigoshop,jigoshop,4000,Jigoshop +manage_jigoshop,jigoshop-ecommerce,400,Jigoshop eCommerce +manage_jigoshop_coupons,jigoshop,4000,Jigoshop +manage_jigoshop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +manage_jigoshop_emails,jigoshop,4000,Jigoshop +manage_jigoshop_orders,jigoshop,4000,Jigoshop +manage_jigoshop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +manage_jigoshop_products,jigoshop,4000,Jigoshop +manage_jigoshop_products,jigoshop-ecommerce,400,Jigoshop eCommerce +manage_job_listing_terms,wp-job-manager,100000,WP Job Manager +manage_job_listings,wp-job-manager,100000,WP Job Manager +manage_jobtitles,employee-directory,400,Staff Directory – Employee Directory for WordPress +manage_klaviyo_shop_cart_terms,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +manage_lana_download_logs,lana-downloads-manager,1000,Lana Downloads Manager +manage_lazyest_files,lazyest-gallery,1000,Lazyest Gallery +manage_leaguemanager,leaguemanager,2000,LeagueManager +manage_leave,hrm,200,WP Human Resource Management +manage_licenses_for_awesome_support,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +manage_lifterlms,lifterlms,8000,LifterLMS +manage_listing_terms,wpcasa,2000,WPCasa +manage_loan,hrm,200,WP Human Resource Management +manage_location,hrm,200,WP Human Resource Management +manage_marital_status,employee-directory,400,Staff Directory – Employee Directory for WordPress +manage_mbdb_book_grids,mooberry-book-manager,1000,Mooberry Book Manager +manage_mbdb_books,mooberry-book-manager,1000,Mooberry Book Manager +manage_mbm,mooberry-book-manager,1000,Mooberry Book Manager +manage_membership_cats,lifterlms,8000,LifterLMS +manage_membership_tags,lifterlms,8000,LifterLMS +manage_module_skins,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +manage_modules,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +manage_mp_menu_item_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +manage_mprm_order_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +manage_mstw_plugins,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +manage_mstw_plugins,team-rosters,800,Team Rosters +manage_mstw_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +manage_mstw_schedules,team-rosters,800,Team Rosters +manage_network,design-approval-system,500,Design Approval System +manage_network_options,design-approval-system,500,Design Approval System +manage_network_themes,design-approval-system,500,Design Approval System +manage_network_users,design-approval-system,500,Design Approval System +manage_news_categories,news-manager,3000,News Manager +manage_news_tags,news-manager,3000,News Manager +manage_newsletter_options,alo-easymail,10000,ALO EasyMail Newsletter +manage_newsletter_subscribers,alo-easymail,10000,ALO EasyMail Newsletter +manage_notice,hrm,200,WP Human Resource Management +manage_notices,notices,400,Notices Ticker +manage_novelist_settings,novelist,800,Novelist +manage_office_locations,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +manage_opalestate_agents_terms,opal-estate,1000,Opal Estate +manage_opalestate_properties_terms,opal-estate,1000,Opal Estate +manage_opalestate_settings,opal-estate,1000,Opal Estate +manage_operations_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +manage_operations_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +manage_operations_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +manage_operations_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +manage_operations_emd_employees,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +manage_operations_emd_persons,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +manage_operations_emd_quotes,request-a-quote,1000,Request a Quote +manage_operations_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +manage_operations_emd_videos,youtube-showcase,7000,YouTube Gallery – Best YouTube Video Gallery for WordPress +manage_orbis,orbis,200,Orbis +manage_orders_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +manage_organization,hrm,200,WP Human Resource Management +manage_others_bookings,events-manager,100000,Events Manager +manage_package_terms,essential-real-estate,3000,Essential Real Estate +manage_patrons,weblibrarian,700,WebLibrarian +manage_payroll,hrm,200,WP Human Resource Management +manage_pbb,peanut-butter-bar-smooth-version,500,Peanut Butter Bar (smooth version) +manage_person_area,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +manage_person_location,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +manage_person_rareas,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +manage_person_title,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +manage_photocontest,wp-photocontest,200,PhotoContest Plugin +manage_photocontests,wp-photocontest,200,PhotoContest Plugin +manage_phpleague,phpleague,300,PHPLeague +manage_podcast,seriously-simple-podcasting,10000,Seriously Simple Podcasting +manage_polls,wp-polls,100000,WP-Polls +manage_popup_categories_terms,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +manage_popup_terms,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +manage_portfolio_categories,custom-content-portfolio,1000,Custom Content Portfolio +manage_portfolio_tags,custom-content-portfolio,1000,Custom Content Portfolio +manage_portfolio_terms,flash-toolkit,30000,Flash Toolkit +manage_portfolio_terms,suffice-toolkit,5000,Suffice Toolkit +manage_postman_smtp,post-smtp,70000,Post SMTP Mailer/Email Log +manage_postman_smtp,postman-smtp,100000,Postman SMTP Mailer/Email Log +manage_postmen,postmen-woo-shipping,800,Postmen WooCommerce Shipping +manage_pricing_rate_terms,awebooking,6000,AweBooking – Hotel Booking System +manage_product,dc-woocommerce-multi-vendor,10000,WC Marketplace +manage_product,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +manage_product_categories,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +manage_product_categories,post-type-x,1000,Product Catalog X +manage_product_categories,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +manage_product_settings,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +manage_product_settings,post-type-x,1000,Product Catalog X +manage_product_tags,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +manage_product_terms,easy-digital-downloads,60000,Easy Digital Downloads +manage_product_terms,jigoshop,4000,Jigoshop +manage_product_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +manage_product_terms,webmaster-user-role,8000,Webmaster User Role +manage_product_terms,woocommerce,4000000,WooCommerce +manage_project_terms,upstream,1000,WordPress Project Management by UpStream +manage_property_terms,essential-real-estate,3000,Essential Real Estate +manage_propertyhive,propertyhive,900,PropertyHive +manage_pta,pta-member-directory,1000,PTA Member Directory and Contact Form +manage_quote_authors,mg-quotes,300,mg Quotes +manage_quote_categories,mg-quotes,300,mg Quotes +manage_raq_services,request-a-quote,1000,Request a Quote +manage_ratings,wp-postratings,100000,WP-PostRatings +manage_real_estate,essential-real-estate,3000,Essential Real Estate +manage_registrations,pta-member-directory,1000,PTA Member Directory and Contact Form +manage_restaurant,restaurant,300,Restaurant +manage_restaurant,restaurant-manager,800,Restaurant Manager +manage_restaurant_menu,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +manage_restaurant_options,restaurant-manager,800,Restaurant Manager +manage_restaurant_settings,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +manage_restaurant_terms,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +manage_restaurantpress,restaurantpress,3000,RestaurantPress +manage_restriction,restaurant-manager,800,Restaurant Manager +manage_resume_organizations,wp-resume,700,WP Resume +manage_resume_sections,wp-resume,700,WP Resume +manage_room_reservation_terms,wp-hotelier,1000,Easy WP Hotelier +manage_room_terms,wp-hotelier,1000,Easy WP Hotelier +manage_room_type_terms,awebooking,6000,AweBooking – Hotel Booking System +manage_rootspersona1,rootspersona,1000,Rootspersona +manage_rootspersona2,rootspersona,1000,Rootspersona +manage_rps_include_content,rps-include-content,2000,RPS Include Content +manage_sales_dash,zero-bs-crm,1000,Zero BS WordPress CRM +manage_salon,salon-booking-system,5000,Salon booking system +manage_saved_search,estatik,2000,Estatik Real Estate Plugin +manage_schema_options,schema,50000,Schema +manage_search_live,search-live,2000,Search Live +manage_sendpress,sendpress,9000,SendPress Newsletters +manage_series,organize-series,4000,Organize Series +manage_series,series,4000,Series +manage_series_terms,mooberry-book-manager,1000,Mooberry Book Manager +manage_settings,hrm,200,WP Human Resource Management +manage_settings_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +manage_sh4,shiftcontroller,600,ShiftController – Employee Shift Scheduling +manage_shop_coupon_terms,jigoshop,4000,Jigoshop +manage_shop_coupon_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +manage_shop_coupon_terms,webmaster-user-role,8000,Webmaster User Role +manage_shop_coupon_terms,woocommerce,4000000,WooCommerce +manage_shop_coupons,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +manage_shop_discount_terms,easy-digital-downloads,60000,Easy Digital Downloads +manage_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +manage_shop_email_terms,jigoshop,4000,Jigoshop +manage_shop_email_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +manage_shop_order_terms,jigoshop,4000,Jigoshop +manage_shop_order_terms,jigoshop-ecommerce,400,Jigoshop eCommerce +manage_shop_order_terms,webmaster-user-role,8000,Webmaster User Role +manage_shop_order_terms,woocommerce,4000000,WooCommerce +manage_shop_payment_terms,easy-digital-downloads,60000,Easy Digital Downloads +manage_shop_settings,easy-digital-downloads,60000,Easy Digital Downloads +manage_shop_webhook_terms,woocommerce,4000000,WooCommerce +manage_signup_sheets,pta-member-directory,1000,PTA Member Directory and Contact Form +manage_signup_sheets,pta-volunteer-sign-up-sheets,2000,PTA Volunteer Sign Up Sheets +manage_signup_sheets,sign-up-sheets,1000,Sign-up Sheets +manage_site_editor_preset,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +manage_site_media,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +manage_sites,design-approval-system,500,Design Approval System +manage_slp,store-locator-le,10000,Store Locator Plus™ for WordPress +manage_slp_admin,store-locator-le,10000,Store Locator Plus™ for WordPress +manage_slp_user,store-locator-le,10000,Store Locator Plus™ for WordPress +manage_snippets,code-snippets,100000,Code Snippets +manage_sp_calendar_terms,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sp_config_terms,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sp_event_terms,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sp_list_terms,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sp_player_terms,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sp_staff_terms,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sp_table_terms,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sp_team_terms,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sportspress,sportspress,20000,SportsPress – Sports Club & League Manager +manage_sprout_invoices_importer,sprout-invoices,2000,Client Invoicing by Sprout Invoices – Easy Estimates and Invoices for WordPress +manage_sprout_invoices_options,sprout-invoices,2000,Client Invoicing by Sprout Invoices – Easy Estimates and Invoices for WordPress +manage_sprout_invoices_payments,sprout-invoices,2000,Client Invoicing by Sprout Invoices – Easy Estimates and Invoices for WordPress +manage_sprout_invoices_records,sprout-invoices,2000,Client Invoicing by Sprout Invoices – Easy Estimates and Invoices for WordPress +manage_store_settings,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +manage_students,pta-member-directory,1000,PTA Member Directory and Contact Form +manage_suffice_toolkit,suffice-toolkit,5000,Suffice Toolkit +manage_support,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +manage_support_plus_agent,wp-support-plus-responsive-ticket-system,10000,WP Support Plus Responsive Ticket System +manage_support_plus_ticket,wp-support-plus-responsive-ticket-system,10000,WP Support Plus Responsive Ticket System +manage_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +manage_syn_rest_course,restaurant-manager,800,Restaurant Manager +manage_syn_rest_cuisine,restaurant-manager,800,Restaurant Manager +manage_syn_rest_diet,restaurant-manager,800,Restaurant Manager +manage_syn_rest_menu,restaurant-manager,800,Restaurant Manager +manage_tag_terms,mooberry-book-manager,1000,Mooberry Book Manager +manage_task_manager,task-manager,400,Task Manager +manage_taxonomies,cms-press,1000,CMS Press +manage_tdih_events,this-day-in-history,800,This Day In History +manage_ticket_priority,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +manage_ticket_status,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +manage_ticket_templates_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +manage_ticket_topic,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +manage_ticket_types_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +manage_topic_tags,bbpress,300000,bbPress +manage_tr_teams,team-rosters,800,Team Rosters +manage_trans_log_terms,essential-real-estate,3000,Essential Real Estate +manage_translations,wp-multilang,7000,WP Multilang +manage_upstream,upstream,1000,WordPress Project Management by UpStream +manage_user_karma_settings,comment-popularity,300,Comment Popularity +manage_user_package_terms,essential-real-estate,3000,Essential Real Estate +manage_user_registration,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +manage_user_registration_terms,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +manage_venues,event-organiser,40000,Event Organiser +manage_vosl,vo-locator-the-wp-store-locator,500,VO Store Locator – WP Store Locator Plugin +manage_wallets,wallets,600,Bitcoin and Altcoin Wallets +manage_wd_ads_groups,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +manage_web_invoice,web-invoice,200,Web Invoice – Invoicing and billing for WordPress +manage_whistles,whistles,2000,Whistles +manage_widgets,restrict-widgets,20000,Restrict Widgets +manage_woocommerce,webmaster-user-role,8000,Webmaster User Role +manage_woocommerce,woocommerce,4000000,WooCommerce +manage_woocommerce_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +manage_woocommerce_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +manage_woocommerce_pos,woocommerce-pos,7000,WooCommerce POS +manage_woocommerce_products,design-approval-system,500,Design Approval System +manage_wordpoints_points_types,wordpoints,700,WordPoints +manage_wp2syslog,wp2syslog,200,wp2syslog +manage_wp_athletics,wp-athletics,300,WP Athletics +manage_wp_instagram_gallery,wp-instagram-gallery,500,WP Instagram Gallery +manage_wpclubmanager,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +manage_wpcm_club_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +manage_wpcm_match_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +manage_wpcm_player_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +manage_wpcm_sponsor_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +manage_wpcm_staff_terms,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +manage_wpfc_categories,sermon-manager-for-wordpress,9000,Sermon Manager +manage_wpfc_sm_settings,sermon-manager-for-wordpress,9000,Sermon Manager +manage_wpp_admintools,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +manage_wpp_categories,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +manage_wpp_make_featured,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +manage_wpp_settings,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +manage_wpsdeals_terms,deals-engine,200,Social Deals Engine +manage_wpsdealssales_terms,deals-engine,200,Social Deals Engine +manage_wpshop,wpshop,1000,=== WPshop – eCommerce +manage_wpsl_settings,wp-store-locator,50000,WP Store Locator +manage_xydac_cms,ultimate-cms,200,Ultimate CMS +manage_yop_polls_bans,yop-poll,20000,YOP Poll +manage_yop_polls_imports,yop-poll,20000,YOP Poll +manage_yop_polls_options,yop-poll,20000,YOP Poll +manage_zodiacpress_interps,zodiacpress,400,ZodiacPress +manage_zodiacpress_settings,zodiacpress,400,ZodiacPress +mc_add_events,my-calendar,30000,My Calendar +mc_approve_events,my-calendar,30000,My Calendar +mc_edit_behaviors,my-calendar,30000,My Calendar +mc_edit_cats,my-calendar,30000,My Calendar +mc_edit_locations,my-calendar,30000,My Calendar +mc_edit_settings,my-calendar,30000,My Calendar +mc_edit_styles,my-calendar,30000,My Calendar +mc_edit_templates,my-calendar,30000,My Calendar +mc_manage_events,my-calendar,30000,My Calendar +mc_view_help,my-calendar,30000,My Calendar +mdjm_employee,mobile-dj-manager,200,MDJM Event Management +mdocs-dashboard,memphis-documents-library,5000,Memphis Documents Library +mdocs_allow_upload,memphis-documents-library,5000,Memphis Documents Library +mdocs_allow_upload_frontend,memphis-documents-library,5000,Memphis Documents Library +mdocs_batch_delete,memphis-documents-library,5000,Memphis Documents Library +mdocs_batch_edit,memphis-documents-library,5000,Memphis Documents Library +mdocs_batch_move,memphis-documents-library,5000,Memphis Documents Library +mdocs_dashboard,memphis-documents-library,5000,Memphis Documents Library +mdocs_manage_options,memphis-documents-library,5000,Memphis Documents Library +mdocs_manage_settings,memphis-documents-library,5000,Memphis Documents Library +mediatags_assign_terms,media-tags,5000,Media Tags +mediatags_delete_terms,media-tags,5000,Media Tags +mediatags_edit_terms,media-tags,5000,Media Tags +mediatags_manage_role_cap,media-tags,5000,Media Tags +mediatags_manage_terms,media-tags,5000,Media Tags +mediatags_settings,media-tags,5000,Media Tags +microblogposter_who_can_auto_publish,microblog-poster,10000,Microblog Poster – Auto Publish on Social Media +milestone_assigned_to_field,upstream,1000,WordPress Project Management by UpStream +milestone_end_date_field,upstream,1000,WordPress Project Management by UpStream +milestone_milestone_field,upstream,1000,WordPress Project Management by UpStream +milestone_notes_field,upstream,1000,WordPress Project Management by UpStream +milestone_start_date_field,upstream,1000,WordPress Project Management by UpStream +moderate,bbpress,300000,bbPress +moderate_comments_hanaboard-post,hana-board,1000,Hana-Board 하나보드 워드프레스 게시판 +moderate_schreikasten,schreikasten,1000,Schreikasten +modify_ditty_news_ticker_settings,ditty-news-ticker,40000,Ditty News Ticker +mr_edit_ratings,multi-rating,6000,Multi Rating +mt-copy-cart,my-tickets,1000,My Tickets +mt-order-comps,my-tickets,1000,My Tickets +mt-order-expired,my-tickets,1000,My Tickets +mt-verify-ticket,my-tickets,1000,My Tickets +mt-view-reports,my-tickets,1000,My Tickets +mto_admin_overview,meta-tags-optimization,1000,Meta Tags Optimization +mto_how_overview,meta-tags-optimization,1000,Meta Tags Optimization +namaste,namaste-lms,1000,Namaste! LMS +namaste_manage,namaste-lms,1000,Namaste! LMS +newsletters_admin_send_sendtoroles,newsletters-lite,8000,Newsletters +newsletters_autoresponderemails,newsletters-lite,8000,Newsletters +newsletters_autoresponders,newsletters-lite,8000,Newsletters +newsletters_clicks,newsletters-lite,8000,Newsletters +newsletters_emails,newsletters-lite,8000,Newsletters +newsletters_extensions,newsletters-lite,8000,Newsletters +newsletters_extensions_settings,newsletters-lite,8000,Newsletters +newsletters_fields,newsletters-lite,8000,Newsletters +newsletters_forms,newsletters-lite,8000,Newsletters +newsletters_gdpr,newsletters-lite,8000,Newsletters +newsletters_groups,newsletters-lite,8000,Newsletters +newsletters_history,newsletters-lite,8000,Newsletters +newsletters_importexport,newsletters-lite,8000,Newsletters +newsletters_links,newsletters-lite,8000,Newsletters +newsletters_lists,newsletters-lite,8000,Newsletters +newsletters_orders,newsletters-lite,8000,Newsletters +newsletters_queue,newsletters-lite,8000,Newsletters +newsletters_send,newsletters-lite,8000,Newsletters +newsletters_settings,newsletters-lite,8000,Newsletters +newsletters_settings_api,newsletters-lite,8000,Newsletters +newsletters_settings_subscribers,newsletters-lite,8000,Newsletters +newsletters_settings_system,newsletters-lite,8000,Newsletters +newsletters_settings_tasks,newsletters-lite,8000,Newsletters +newsletters_settings_templates,newsletters-lite,8000,Newsletters +newsletters_settings_updates,newsletters-lite,8000,Newsletters +newsletters_submitserial,newsletters-lite,8000,Newsletters +newsletters_subscribers,newsletters-lite,8000,Newsletters +newsletters_support,newsletters-lite,8000,Newsletters +newsletters_templates,newsletters-lite,8000,Newsletters +newsletters_templates_save,newsletters-lite,8000,Newsletters +newsletters_themes,newsletters-lite,8000,Newsletters +newsletters_welcome,newsletters-lite,8000,Newsletters +newsman_wpNewsman,wpnewsman-newsletters,1000,WPNewsman Lite +nextend,smart-slider-3,300000,Smart Slider 3 +nextend_config,smart-slider-3,300000,Smart Slider 3 +nextend_visual_delete,smart-slider-3,300000,Smart Slider 3 +nextend_visual_edit,smart-slider-3,300000,Smart Slider 3 +nicescrollr_edit,nicescrollr,200,Nicescrollr +oQeyGalleries,oqey-gallery,3000,Plugin Name: oQey Gallery +oQeyMusic,oqey-gallery,3000,Plugin Name: oQey Gallery +oQeyRoles,oqey-gallery,3000,Plugin Name: oQey Gallery +oQeySettings,oqey-gallery,3000,Plugin Name: oQey Gallery +oQeySkins,oqey-gallery,3000,Plugin Name: oQey Gallery +oQeyTrash,oqey-gallery,3000,Plugin Name: oQey Gallery +oQeyVideo,oqey-gallery,3000,Plugin Name: oQey Gallery +olimometer_dashboard_widget,olimometer,2000,Olimometer +oqey-gallery,oqey-gallery,3000,Plugin Name: oQey Gallery +override_document_lock,wp-document-revisions,4000,WP Document Revisions +ow_abort_workflow,oasis-workflow,1000,Oasis Workflow +ow_create_workflow,oasis-workflow,1000,Oasis Workflow +ow_delete_workflow,oasis-workflow,1000,Oasis Workflow +ow_delete_workflow_history,oasis-workflow,1000,Oasis Workflow +ow_download_workflow_history,oasis-workflow,1000,Oasis Workflow +ow_edit_workflow,oasis-workflow,1000,Oasis Workflow +ow_reassign_task,oasis-workflow,1000,Oasis Workflow +ow_sign_off_step,oasis-workflow,1000,Oasis Workflow +ow_skip_workflow,oasis-workflow,1000,Oasis Workflow +ow_submit_to_workflow,oasis-workflow,1000,Oasis Workflow +ow_view_others_inbox,oasis-workflow,1000,Oasis Workflow +ow_view_reports,oasis-workflow,1000,Oasis Workflow +ow_view_workflow_history,oasis-workflow,1000,Oasis Workflow +p2pConverter,p2pconverter,2000,p2pConverter +pTypeConverter,ptypeconverter,7000,pTypeConverter +participate,bbpress,300000,bbPress +payroll_revistion,hrm,200,WP Human Resource Management +pc_manage_permalink_redirects,permalinks-customizer,3000,Permalinks Customizer +pc_manage_permalink_settings,permalinks-customizer,3000,Permalinks Customizer +pc_manage_permalinks,permalinks-customizer,3000,Permalinks Customizer +phpleague,phpleague,300,PHPLeague +picasa_dialog,photo-express-for-google,2000,Photo Express for Google +picasa_dialog,picasa-express-x2,4000,Picasa and Google Plus Express +pipe_access_embed,pipe-video-recorder,300,Pipe Video Recorder +pipe_access_plugin,pipe-video-recorder,300,Pipe Video Recorder +pipe_access_record,pipe-video-recorder,300,Pipe Video Recorder +pipe_access_recordings,pipe-video-recorder,300,Pipe Video Recorder +pipe_access_setup,pipe-video-recorder,300,Pipe Video Recorder +pmpro_addons,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_advancedsettings,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_discountcodes,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_edit_memberships,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_emailsettings,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_membershiplevels,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_memberships_menu,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_memberslist,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_memberslistcsv,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_orders,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_orderscsv,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_pagesettings,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_paymentsettings,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_reports,paid-memberships-pro,70000,Paid Memberships Pro +pmpro_updates,paid-memberships-pro,70000,Paid Memberships Pro +podlove_read_analytics,podlove-podcasting-plugin-for-wordpress,4000,Podlove Podcast Publisher +podlove_read_dashboard,podlove-podcasting-plugin-for-wordpress,4000,Podlove Podcast Publisher +post_avatars,post-avatar,900,Post Avatar +post_pay_counter_access_stats,post-pay-counter,1000,Post Pay Counter +post_pay_counter_manage_options,post-pay-counter,1000,Post Pay Counter +post_via_postie,postie,20000,Postie +pp_administer_content,press-permit-core,5000,Press Permit Core +pp_assign_roles,press-permit-core,5000,Press Permit Core +pp_create_groups,press-permit-core,5000,Press Permit Core +pp_delete_groups,press-permit-core,5000,Press Permit Core +pp_edit_groups,press-permit-core,5000,Press Permit Core +pp_manage_members,press-permit-core,5000,Press Permit Core +pp_manage_roles,publishpress,1000,PublishPress – Professional publishing tools for WordPress +pp_manage_settings,press-permit-core,5000,Press Permit Core +pp_moderate_any,press-permit-core,5000,Press Permit Core +pp_set_read_exceptions,press-permit-core,5000,Press Permit Core +pp_view_calendar,publishpress,1000,PublishPress – Professional publishing tools for WordPress +pp_view_content_overview,publishpress,1000,PublishPress – Professional publishing tools for WordPress +pp_view_story_budget,publishpress,1000,PublishPress – Professional publishing tools for WordPress +prepare_ebay_listings,wp-lister-for-ebay,5000,WP-Lister Lite for eBay +project_client_field,upstream,1000,WordPress Project Management by UpStream +project_end_date_field,upstream,1000,WordPress Project Management by UpStream +project_owner_field,upstream,1000,WordPress Project Management by UpStream +project_send_newsletter,projectmanager,400,ProjectManager +project_start_date_field,upstream,1000,WordPress Project Management by UpStream +project_status_field,upstream,1000,WordPress Project Management by UpStream +project_title_field,upstream,1000,WordPress Project Management by UpStream +project_users_field,upstream,1000,WordPress Project Management by UpStream +projectmanager_send_confirmation,projectmanager,400,ProjectManager +projectmanager_settings,projectmanager,400,ProjectManager +projectmanager_user,projectmanager,400,ProjectManager +promote_users_higher_level,wpfront-user-role-editor,60000,WPFront User Role Editor +promote_users_to_higher_level,wpfront-user-role-editor,60000,WPFront User Role Editor +pronamic_client,pronamic-client,300,Pronamic Client +publish_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +publish_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +publish_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +publish_achievement_progresses,achievements,300,Achievements for WordPress +publish_achievements,achievements,300,Achievements for WordPress +publish_ads,apply-online,5000,Apply Online +publish_aec_events,another-events-calendar,800,Another Events Calendar +publish_aec_organizers,another-events-calendar,800,Another Events Calendar +publish_aec_venues,another-events-calendar,800,Another Events Calendar +publish_affiliate_keywords,affiliate,700,Affiliate +publish_agents,essential-real-estate,3000,Essential Real Estate +publish_aggregator-records,the-events-calendar,700000,The Events Calendar +publish_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +publish_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +publish_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +publish_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +publish_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +publish_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +publish_anbs,alert-notice-boxes,1000,Alert Notice Boxes +publish_applications,apply-online,5000,Apply Online +publish_archivs,archive,700,Archive +publish_articles,issuem,1000,IssueM +publish_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +publish_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +publish_awebookings,awebooking,6000,AweBooking – Hotel Booking System +publish_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +publish_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +publish_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +publish_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +publish_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +publish_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +publish_blocks,gutenberg,500000,Gutenberg +publish_board_committees,nonprofit-board-management,400,Nonprofit Board Management +publish_board_events,nonprofit-board-management,400,Nonprofit Board Management +publish_book-reviews,book-review-library,700,Book Review Library +publish_books,novelist,800,Novelist +publish_boxes,boxzilla,20000,Boxzilla +publish_bps_forms,bp-profile-search,10000,BP Profile Search +publish_calp_events,calpress-event-calendar,5000,CalPress Calendar +publish_campaigns,charitable,10000,Charitable – Donation Plugin +publish_campaigns,leyka,1000,Leyka +publish_car_listings,wp-car-manager,3000,WP Car Manager +publish_cctor_coupons,coupon-creator,10000,Coupon Creator +publish_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +publish_classified_listings,classifieds-wp,800,Classifieds WP +publish_clients,upstream,1000,WordPress Project Management by UpStream +publish_courses,lifterlms,8000,LifterLMS +publish_ctas,cta,10000,WordPress Calls to Action +publish_cupri_pays,pardakht-delkhah,1000,پلاگین پرداخت دلخواه +publish_custom_csss,custom-css-js,100000,Simple Custom CSS and JS +publish_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +publish_documents,wp-document-revisions,4000,WP Document Revisions +publish_donations,charitable,10000,Charitable – Donation Plugin +publish_donations,leyka,1000,Leyka +publish_dsn_notes,admin-dashboard-site-notes,3000,Dashboard Site Notes +publish_ebay_listings,wp-lister-for-ebay,5000,WP-Lister Lite for eBay +publish_edr_courses,educator,1000,Educator 2 +publish_edr_lessons,educator,1000,Educator 2 +publish_edr_memberships,educator,1000,Educator 2 +publish_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +publish_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +publish_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +publish_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +publish_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +publish_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +publish_emd_quotes,request-a-quote,1000,Request a Quote +publish_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +publish_epa_albums,easy-photo-album,5000,Easy Photo Album +publish_event_listings,wp-event-manager,1000,WP Event Manager +publish_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +publish_events,event-organiser,40000,Event Organiser +publish_events,events-maker,4000,Events Maker by dFactory +publish_events,events-manager,100000,Events Manager +publish_events,quick-event-manager,5000,Quick Event Manager +publish_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +publish_fa_items,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +publish_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +publish_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +publish_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +publish_fep_announcements,front-end-pm,8000,Front End PM +publish_fep_messages,front-end-pm,8000,Front End PM +publish_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +publish_food_groups,restaurantpress,3000,RestaurantPress +publish_food_menus,restaurantpress,3000,RestaurantPress +publish_foosales,foosales,400,FooSales +publish_forms,pronamic-ideal,6000,Pronamic Pay +publish_forums,bbpress,300000,bbPress +publish_galleries,gallery-box,2000,Gallery Box +publish_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +publish_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +publish_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +publish_glossaries,glossary-by-codeat,1000,Glossary +publish_hanaboard-post,hana-board,1000,Hana-Board 하나보드 워드프레스 게시판 +publish_hb_bookings,wp-hotel-booking,7000,WP Hotel Booking +publish_hb_rooms,wp-hotel-booking,7000,WP Hotel Booking +publish_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +publish_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +publish_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +publish_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +publish_ib_edu_memberships,ibeducator,1000,Educator +publish_ib_educator_courses,ibeducator,1000,Educator +publish_ib_educator_lessons,ibeducator,1000,Educator +publish_ims_gallerys,image-store,900,Image Store +publish_inbound-form,cta,10000,WordPress Calls to Action +publish_inbound-form,landing-pages,10000,WordPress Landing Pages +publish_inbound-form,leads,7000,WordPress Leads +publish_insertcodes,insert-code,300,Insert Code +publish_invoices,essential-real-estate,3000,Essential Real Estate +publish_issues,issuem,1000,IssueM +publish_items,gamipress,2000,GamiPress +publish_job_listings,wp-job-manager,100000,WP Job Manager +publish_jscp_match,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +publish_jscp_player,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +publish_jscp_team,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +publish_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +publish_landing_pages,landing-pages,10000,WordPress Landing Pages +publish_languages,sublanguage,1000,Sublanguage +publish_leads,cta,10000,WordPress Calls to Action +publish_leads,landing-pages,10000,WordPress Landing Pages +publish_leads,leads,7000,WordPress Leads +publish_legalpack_pages,legalpack,400,Legalpack +publish_lessons,lifterlms,8000,LifterLMS +publish_listings,auto-listings,400,Auto Listings +publish_listings,wp-real-estate,400,WP Real Estate +publish_listings,wpcasa,2000,WPCasa +publish_locations,events-manager,100000,Events Manager +publish_lp_courses,learnpress,50000,LearnPress – WordPress LMS Plugin +publish_lp_lessons,learnpress,50000,LearnPress – WordPress LMS Plugin +publish_lp_orders,learnpress,50000,LearnPress – WordPress LMS Plugin +publish_masterslider,master-slider,100000,Master Slider – Responsive Touch Slider +publish_mbdb_book,mooberry-book-manager,1000,Mooberry Book Manager +publish_mbdb_book_grid,mooberry-book-manager,1000,Mooberry Book Manager +publish_mbdb_book_grids,mooberry-book-manager,1000,Mooberry Book Manager +publish_mbdb_books,mooberry-book-manager,1000,Mooberry Book Manager +publish_meals,restaurant-manager,800,Restaurant Manager +publish_memberships,lifterlms,8000,LifterLMS +publish_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +publish_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +publish_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +publish_nemus-sliders,nemus-slider,2000,Nemus Slider +publish_news,news-manager,3000,News Manager +publish_newsletters,alo-easymail,10000,ALO EasyMail Newsletter +publish_niso_slider_carousels,niso-carousel,200,Niso Carousel +publish_niso_slider_carousels,niso-carousel-slider,400,Niso Carousel Slider +publish_opalestate_agentss,opal-estate,1000,Opal Estate +publish_opalestate_propertiess,opal-estate,1000,Opal Estate +publish_opanda-items,opt-in-panda,3000,OnePress Opt-In Panda +publish_opanda-items,social-locker,10000,OnePress Social Locker +publish_orbis_companies,orbis,200,Orbis +publish_orbis_projects,orbis,200,Orbis +publish_packages,essential-real-estate,3000,Essential Real Estate +publish_page_in_section,bu-section-editing,300,BU Section Editing +publish_players,team-rosters,800,Team Rosters +publish_playlists,radio-station,1000,Radio Station +publish_plugin_filters,plugin-organizer,10000,Plugin Organizer +publish_plugin_groups,plugin-organizer,10000,Plugin Organizer +publish_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +publish_portfolios,flash-toolkit,30000,Flash Toolkit +publish_portfolios,suffice-toolkit,5000,Suffice Toolkit +publish_portfolios,visual-portfolio,7000,Visual Portfolio +publish_post_in_section,bu-section-editing,300,BU Section Editing +publish_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +publish_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +publish_products,design-approval-system,500,Design Approval System +publish_products,easy-digital-downloads,60000,Easy Digital Downloads +publish_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +publish_products,gnucommerce,1000,GNUCommerce +publish_products,jigoshop,4000,Jigoshop +publish_products,jigoshop-ecommerce,400,Jigoshop eCommerce +publish_products,post-type-x,1000,Product Catalog X +publish_products,products,300,WP Products +publish_products,webmaster-user-role,8000,Webmaster User Role +publish_products,woocommerce,4000000,WooCommerce +publish_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +publish_products,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +publish_profile_cct,profile-custom-content-type,200,Profile CCT +publish_project_bugs,upstream,1000,WordPress Project Management by UpStream +publish_project_discussion,upstream,1000,WordPress Project Management by UpStream +publish_project_files,upstream,1000,WordPress Project Management by UpStream +publish_project_milestones,upstream,1000,WordPress Project Management by UpStream +publish_project_tasks,upstream,1000,WordPress Project Management by UpStream +publish_projects,upstream,1000,WordPress Project Management by UpStream +publish_propertys,essential-real-estate,3000,Essential Real Estate +publish_psp_projects,project-panorama-lite,1000,Project Panorama +publish_questions,lifterlms,8000,LifterLMS +publish_quizzes,lifterlms,8000,LifterLMS +publish_quotes,mg-quotes,300,mg Quotes +publish_recurring_events,events-manager,100000,Events Manager +publish_redirects,wp-redirects,700,WP Redirects +publish_rem_properties,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +publish_replies,bbpress,300000,bbPress +publish_reservations,restaurant-manager,800,Restaurant Manager +publish_resume_positions,wp-resume,700,WP Resume +publish_room_reservations,wp-hotelier,1000,Easy WP Hotelier +publish_room_types,awebooking,6000,AweBooking – Hotel Booking System +publish_rooms,wp-hotelier,1000,Easy WP Hotelier +publish_rsvpemails,rsvpmaker,1000,RSVPMaker +publish_rsvpmakers,rsvpmaker,1000,RSVPMaker +publish_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +publish_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +publish_shifts,employee-scheduler,400,Shiftee Basic – Employee and Staff Scheduling +publish_shop_coupons,jigoshop,4000,Jigoshop +publish_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +publish_shop_coupons,webmaster-user-role,8000,Webmaster User Role +publish_shop_coupons,woocommerce,4000000,WooCommerce +publish_shop_coupons,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +publish_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +publish_shop_emails,jigoshop,4000,Jigoshop +publish_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +publish_shop_orders,jigoshop,4000,Jigoshop +publish_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +publish_shop_orders,webmaster-user-role,8000,Webmaster User Role +publish_shop_orders,woocommerce,4000000,WooCommerce +publish_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +publish_shop_webhooks,woocommerce,4000000,WooCommerce +publish_shows,radio-station,1000,Radio Station +publish_sln_attendants,salon-booking-system,5000,Salon booking system +publish_sln_bookings,salon-booking-system,5000,Salon booking system +publish_sln_services,salon-booking-system,5000,Salon booking system +publish_snippets,wp-snippets,1000,WP Snippets +publish_sola_st_tickets,sola-support-tickets,400,Sola Support Tickets +publish_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +publish_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +publish_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +publish_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +publish_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +publish_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +publish_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +publish_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +publish_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +publish_sprout_invoices,sprout-invoices,2000,Client Invoicing by Sprout Invoices – Easy Estimates and Invoices for WordPress +publish_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +publish_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +publish_stores,wp-store-locator,50000,WP Store Locator +publish_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +publish_sunshine_gallery,sunshine-photo-cart,2000,Sunshine Photo Cart +publish_sunshine_order,sunshine-photo-cart,2000,Sunshine Photo Cart +publish_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +publish_sunshine_product,sunshine-photo-cart,2000,Sunshine Photo Cart +publish_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +publish_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +publish_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +publish_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +publish_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +publish_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +publish_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +publish_tm-propertys,cherry-real-estate,600,Cherry Real Estate +publish_topics,bbpress,300000,bbPress +publish_total_slider_slides,total-slider,500,Total Slider +publish_trans_logs,essential-real-estate,3000,Essential Real Estate +publish_translations,simple-punctual-translation,200,Simple Punctual Translation +publish_tribe_events,the-events-calendar,700000,The Events Calendar +publish_tribe_organizers,the-events-calendar,700000,The Events Calendar +publish_tribe_venues,the-events-calendar,700000,The Events Calendar +publish_un_feedback,usernoise,7000,Usernoise modal feedback / contact form +publish_un_feedback_items,usernoise,7000,Usernoise modal feedback / contact form +publish_user_packages,essential-real-estate,3000,Essential Real Estate +publish_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +publish_vacancies,job-board,300,Job Board by BestWebSoft +publish_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +publish_wbcr-snippetss,insert-php,100000,PHP code snippets (Insert PHP) +publish_wctrl_contents,widgets-control,1000,Widgets Control +publish_wd_ads_adverts,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +publish_wd_ads_schedules,ad-manager-wd,700,Ad Manager by WD – Advanced Ad Manager plugin +publish_wiki_pages,wordpress-wiki,400,WordPress Wiki +publish_wordlift_entities,wordlift,400,WordLift – AI powered SEO +publish_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +publish_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +publish_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +publish_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +publish_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +publish_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +publish_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +publish_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +publish_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +publish_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +publish_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +publish_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +publish_wplc_quick_response,wp-live-chat-support,60000,WP Live Chat Support +publish_wpp_properties,wp-property,5000,WP-Property – WordPress Powered Real Estate and Property Management +publish_wppizzas,wppizza,2000,WPPizza +publish_wprm_reservations,wp-restaurant-manager,700,WP Restaurant Manager +publish_wpsdealss,deals-engine,200,Social Deals Engine +publish_wpsdealssaless,deals-engine,200,Social Deals Engine +publish_wpse_profiles,wp-smart-editor,900,WP Smart Editor +publish_wswebinars,wp-webinarsystem,2000,WP WebinarSystem +publish_ycd_countdowns,countdown-builder,1000,Countdown +push_crossword,crosswordsearch,300,crosswordsearch +read_acadp_field,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +read_acadp_listing,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +read_acadp_payment,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +read_achievements,achievements,300,Achievements for WordPress +read_aec_event,another-events-calendar,800,Another Events Calendar +read_aec_organizer,another-events-calendar,800,Another Events Calendar +read_aec_venue,another-events-calendar,800,Another Events Calendar +read_agent,essential-real-estate,3000,Essential Real Estate +read_ai1ec_event,all-in-one-event-calendar,100000,All-in-One Event Calendar +read_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +read_aiovg_video,all-in-one-video-gallery,1000,All-in-One Video Gallery +read_all_touchpoints,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +read_all_ukuupeoples,ukuupeople-the-simple-crm,1000,CRM: Contact Management Simplified – UkuuPeople +read_application,apply-online,5000,Apply Online +read_archiv,archive,700,Archive +read_article,issuem,1000,IssueM +read_at_biz_dir,directorist,500,Directorist – Business Directory Plugin +read_atbdp_order,directorist,500,Directorist – Business Directory Plugin +read_awebooking,awebooking,6000,AweBooking – Hotel Booking System +read_birs_appointment,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_birs_client,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_birs_location,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_birs_payment,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_birs_service,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_birs_staff,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_blocks,gutenberg,500000,Gutenberg +read_book,novelist,800,Novelist +read_book-reviews,book-review-library,700,Book Review Library +read_box,boxzilla,20000,Boxzilla +read_calp_event,calpress-event-calendar,5000,CalPress Calendar +read_campaign,charitable,10000,Charitable – Donation Plugin +read_campaign,leyka,1000,Leyka +read_car_listing,wp-car-manager,3000,WP Car Manager +read_cctor_coupon,coupon-creator,10000,Coupon Creator +read_chronosly,chronosly-events-calendar,4000,Chronosly Events Calendar +read_classified_listing,classifieds-wp,800,Classifieds WP +read_client,upstream,1000,WordPress Project Management by UpStream +read_course,lifterlms,8000,LifterLMS +read_cta,cta,10000,WordPress Calls to Action +read_cupri_pay,pardakht-delkhah,1000,پلاگین پرداخت دلخواه +read_custom_css,custom-css-js,100000,Simple Custom CSS and JS +read_ditty_news_ticker,ditty-news-ticker,40000,Ditty News Ticker +read_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +read_document_revisions,wp-document-revisions,4000,WP Document Revisions +read_documents,wp-document-revisions,4000,WP Document Revisions +read_donation,charitable,10000,Charitable – Donation Plugin +read_donation,leyka,1000,Leyka +read_dsn_note,admin-dashboard-site-notes,3000,Dashboard Site Notes +read_edr_course,educator,1000,Educator 2 +read_edr_lesson,educator,1000,Educator 2 +read_edr_membership,educator,1000,Educator 2 +read_email_template,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +read_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +read_event,quick-event-manager,5000,Quick Event Manager +read_event_listing,wp-event-manager,1000,WP Event Manager +read_event_magic,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +read_everest_form,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +read_feed,wp-rss-aggregator,60000,WP RSS Aggregator +read_feed_source,wp-rss-aggregator,60000,WP RSS Aggregator +read_flexible_invoice,flexible-invoices,1000,Flexible Invoices for WordPress +read_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +read_food_group,restaurantpress,3000,RestaurantPress +read_food_menu,restaurantpress,3000,RestaurantPress +read_form,formlift,800,FormLift for Infusionsoft Web Forms +read_form,pronamic-ideal,6000,Pronamic Pay +read_galleries,gallery-box,2000,Gallery Box +read_game,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_give_form,give,50000,Give – Donation Plugin and Fundraising Platform +read_give_payment,give,50000,Give – Donation Plugin and Fundraising Platform +read_glossary,glossary-by-codeat,1000,Glossary +read_hanaboard-post,hana-board,1000,Hana-Board 하나보드 워드프레스 게시판 +read_hf_membership_plan,xa-woocommerce-memberships,400,Memberships for WooCommerce +read_hf_user_membership,xa-woocommerce-memberships,400,Memberships for WooCommerce +read_hidden_forums,bbpress,300000,bbPress +read_hotel_location,awebooking,6000,AweBooking – Hotel Booking System +read_hotel_service,awebooking,6000,AweBooking – Hotel Booking System +read_ib_edu_membership,ibeducator,1000,Educator +read_ib_educator_course,ibeducator,1000,Educator +read_ib_educator_lesson,ibeducator,1000,Educator +read_ims_gallery,image-store,900,Image Store +read_inbound-form,cta,10000,WordPress Calls to Action +read_inbound-form,landing-pages,10000,WordPress Landing Pages +read_inbound-form,leads,7000,WordPress Leads +read_insertcode,insert-code,300,Insert Code +read_invoice,essential-real-estate,3000,Essential Real Estate +read_item,gamipress,2000,GamiPress +read_job_listing,wp-job-manager,100000,WP Job Manager +read_jscp_match,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +read_jscp_player,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +read_jscp_team,joomsport-sports-league-results-management,1000,"JoomSport – for Sports: Team & League, Football, Hockey & more" +read_klaviyo_shop_cart,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +read_landing_page,landing-pages,10000,WordPress Landing Pages +read_language,sublanguage,1000,Sublanguage +read_lead,cta,10000,WordPress Calls to Action +read_lead,landing-pages,10000,WordPress Landing Pages +read_lead,leads,7000,WordPress Leads +read_legalpack_pages,legalpack,400,Legalpack +read_lesson,lifterlms,8000,LifterLMS +read_listing,auto-listings,400,Auto Listings +read_listing,wp-real-estate,400,WP Real Estate +read_listing,wpcasa,2000,WPCasa +read_membership,lifterlms,8000,LifterLMS +read_mp_menu_item,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +read_mprm_order,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +read_nc_reference,nelio-content,7000,Nelio Content – Social Media Marketing Automation +read_nemus-slider,nemus-slider,2000,Nemus Slider +read_niso_private_carousels_slider,niso-carousel,200,Niso Carousel +read_niso_private_carousels_slider,niso-carousel-slider,400,Niso Carousel Slider +read_niso_slider_carousels,niso-carousel,200,Niso Carousel +read_niso_slider_carousels,niso-carousel-slider,400,Niso Carousel Slider +read_opalestate_agents,opal-estate,1000,Opal Estate +read_opalestate_properties,opal-estate,1000,Opal Estate +read_opanda-item,opt-in-panda,3000,OnePress Opt-In Panda +read_opanda-item,social-locker,10000,OnePress Social Locker +read_orbis_company,orbis,200,Orbis +read_orbis_project,orbis,200,Orbis +read_others_attachments,wpfront-user-role-editor,60000,WPFront User Role Editor +read_others_locations,events-manager,100000,Events Manager +read_package,essential-real-estate,3000,Essential Real Estate +read_payment,pronamic-ideal,6000,Pronamic Pay +read_player,team-rosters,800,Team Rosters +read_playlists,radio-station,1000,Radio Station +read_plugin_filters,plugin-organizer,10000,Plugin Organizer +read_plugin_groups,plugin-organizer,10000,Plugin Organizer +read_portfolio,flash-toolkit,30000,Flash Toolkit +read_portfolio,suffice-toolkit,5000,Suffice Toolkit +read_portfolio,visual-portfolio,7000,Visual Portfolio +read_post,countdown-builder,1000,Countdown +read_post,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +read_pricing_rate,awebooking,6000,AweBooking – Hotel Booking System +read_private_acadp_fields,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +read_private_acadp_listings,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +read_private_acadp_payments,advanced-classifieds-and-directory-pro,4000,Advanced Classifieds & Directory Pro +read_private_achievement_progresses,achievements,300,Achievements for WordPress +read_private_achievements,achievements,300,Achievements for WordPress +read_private_ads,apply-online,5000,Apply Online +read_private_aec_events,another-events-calendar,800,Another Events Calendar +read_private_aec_organizers,another-events-calendar,800,Another Events Calendar +read_private_aec_venues,another-events-calendar,800,Another Events Calendar +read_private_affiliate_keywords,affiliate,700,Affiliate +read_private_agents,essential-real-estate,3000,Essential Real Estate +read_private_aggregator-records,the-events-calendar,700000,The Events Calendar +read_private_ai1ec_events,all-in-one-event-calendar,100000,All-in-One Event Calendar +read_private_aiovg_videos,all-in-one-video-gallery,1000,All-in-One Video Gallery +read_private_anb_animations,alert-notice-boxes,1000,Alert Notice Boxes +read_private_anb_animations_out,alert-notice-boxes,1000,Alert Notice Boxes +read_private_anb_designs,alert-notice-boxes,1000,Alert Notice Boxes +read_private_anb_locations,alert-notice-boxes,1000,Alert Notice Boxes +read_private_anbs,alert-notice-boxes,1000,Alert Notice Boxes +read_private_applications,apply-online,5000,Apply Online +read_private_archivs,archive,700,Archive +read_private_articles,issuem,1000,IssueM +read_private_at_biz_dirs,directorist,500,Directorist – Business Directory Plugin +read_private_atbdp_orders,directorist,500,Directorist – Business Directory Plugin +read_private_awebookings,awebooking,6000,AweBooking – Hotel Booking System +read_private_birs_appointments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_private_birs_clients,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_private_birs_locations,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_private_birs_payments,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_private_birs_services,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_private_birs_staffs,birchschedule,8000,Appointment Booking Calendar – BirchPress Scheduler +read_private_blocks,gutenberg,500000,Gutenberg +read_private_board_committees,nonprofit-board-management,400,Nonprofit Board Management +read_private_board_events,nonprofit-board-management,400,Nonprofit Board Management +read_private_books,novelist,800,Novelist +read_private_box,boxzilla,20000,Boxzilla +read_private_calp_events,calpress-event-calendar,5000,CalPress Calendar +read_private_campaigns,charitable,10000,Charitable – Donation Plugin +read_private_campaigns,leyka,1000,Leyka +read_private_car_listings,wp-car-manager,3000,WP Car Manager +read_private_cctor_coupons,coupon-creator,10000,Coupon Creator +read_private_chronoslys,chronosly-events-calendar,4000,Chronosly Events Calendar +read_private_classified_listings,classifieds-wp,800,Classifieds WP +read_private_clients,upstream,1000,WordPress Project Management by UpStream +read_private_courses,lifterlms,8000,LifterLMS +read_private_ctas,cta,10000,WordPress Calls to Action +read_private_cupri_pays,pardakht-delkhah,1000,پلاگین پرداخت دلخواه +read_private_ditty_news_tickers,ditty-news-ticker,40000,Ditty News Ticker +read_private_documents,wp-document-revisions,4000,WP Document Revisions +read_private_donations,charitable,10000,Charitable – Donation Plugin +read_private_donations,leyka,1000,Leyka +read_private_dsn_notes,admin-dashboard-site-notes,3000,Dashboard Site Notes +read_private_edr_courses,educator,1000,Educator 2 +read_private_edr_lessons,educator,1000,Educator 2 +read_private_edr_memberships,educator,1000,Educator 2 +read_private_email_templates,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +read_private_emails,mailer-dragon,300,Mailer Dragon – Email Marketing Plugin for WordPress +read_private_emd_agents,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +read_private_emd_canned_responses,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +read_private_emd_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +read_private_emd_employees,employee-directory,400,Staff Directory – Employee Directory for WordPress +read_private_emd_quotes,request-a-quote,1000,Request a Quote +read_private_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +read_private_epa_albums,easy-photo-album,5000,Easy Photo Album +read_private_event,quick-event-manager,5000,Quick Event Manager +read_private_event_listings,wp-event-manager,1000,WP Event Manager +read_private_event_magics,kikfyre-events-calendar-tickets,200,"Events, Calendars & Tickets – Event Kikfyre" +read_private_events,event-organiser,40000,Event Organiser +read_private_events,events-maker,4000,Events Maker by dFactory +read_private_events,events-manager,100000,Events Manager +read_private_everest_forms,everest-forms,40000,Everest Forms – Easy Contact Form and Form Builder for WordPress +read_private_fa_items,featured-articles-lite,3000,FA Lite – WP responsive slider plugin +read_private_fbtabs,facebook-tab-manager,1000,Facebook Tab Manager +read_private_feed_sources,wp-rss-aggregator,60000,WP RSS Aggregator +read_private_feeds,wp-rss-aggregator,60000,WP RSS Aggregator +read_private_fep_announcements,front-end-pm,8000,Front End PM +read_private_fep_messages,front-end-pm,8000,Front End PM +read_private_flexible_invoices,flexible-invoices,1000,Flexible Invoices for WordPress +read_private_food_groups,restaurantpress,3000,RestaurantPress +read_private_food_menus,restaurantpress,3000,RestaurantPress +read_private_forms,pronamic-ideal,6000,Pronamic Pay +read_private_forums,bbpress,300000,bbPress +read_private_galleries,gallery-box,2000,Gallery Box +read_private_games,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_private_give_forms,give,50000,Give – Donation Plugin and Fundraising Platform +read_private_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +read_private_glossaries,glossary-by-codeat,1000,Glossary +read_private_hf_membership_plans,xa-woocommerce-memberships,400,Memberships for WooCommerce +read_private_hf_user_memberships,xa-woocommerce-memberships,400,Memberships for WooCommerce +read_private_hotel_locations,awebooking,6000,AweBooking – Hotel Booking System +read_private_hotel_services,awebooking,6000,AweBooking – Hotel Booking System +read_private_ib_edu_memberships,ibeducator,1000,Educator +read_private_ib_educator_courses,ibeducator,1000,Educator +read_private_ib_educator_lessons,ibeducator,1000,Educator +read_private_ims_gallery,image-store,900,Image Store +read_private_inbound-forms,cta,10000,WordPress Calls to Action +read_private_inbound-forms,landing-pages,10000,WordPress Landing Pages +read_private_inbound-forms,leads,7000,WordPress Leads +read_private_insertcodes,insert-code,300,Insert Code +read_private_invoices,essential-real-estate,3000,Essential Real Estate +read_private_items,gamipress,2000,GamiPress +read_private_job_listings,wp-job-manager,100000,WP Job Manager +read_private_klaviyo_shop_carts,klaviyo-for-woocommerce,1000,Klaviyo for WooCommerce +read_private_landing_pages,landing-pages,10000,WordPress Landing Pages +read_private_languages,sublanguage,1000,Sublanguage +read_private_leads,cta,10000,WordPress Calls to Action +read_private_leads,landing-pages,10000,WordPress Landing Pages +read_private_leads,leads,7000,WordPress Leads +read_private_legalpack_pages,legalpack,400,Legalpack +read_private_lessons,lifterlms,8000,LifterLMS +read_private_listings,auto-listings,400,Auto Listings +read_private_listings,wp-real-estate,400,WP Real Estate +read_private_listings,wpcasa,2000,WPCasa +read_private_locations,events-manager,100000,Events Manager +read_private_meals,restaurant-manager,800,Restaurant Manager +read_private_memberships,lifterlms,8000,LifterLMS +read_private_mp_menu_items,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +read_private_mprm_orders,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +read_private_nc_references,nelio-content,7000,Nelio Content – Social Media Marketing Automation +read_private_nemus-sliders,nemus-slider,2000,Nemus Slider +read_private_news,news-manager,3000,News Manager +read_private_newsletters,alo-easymail,10000,ALO EasyMail Newsletter +read_private_opalestate_agentss,opal-estate,1000,Opal Estate +read_private_opalestate_propertiess,opal-estate,1000,Opal Estate +read_private_opanda-items,opt-in-panda,3000,OnePress Opt-In Panda +read_private_opanda-items,social-locker,10000,OnePress Social Locker +read_private_orbis_companies,orbis,200,Orbis +read_private_orbis_projects,orbis,200,Orbis +read_private_packages,essential-real-estate,3000,Essential Real Estate +read_private_payments,pronamic-ideal,6000,Pronamic Pay +read_private_players,team-rosters,800,Team Rosters +read_private_plugin_filters,plugin-organizer,10000,Plugin Organizer +read_private_plugin_groups,plugin-organizer,10000,Plugin Organizer +read_private_portfolio_projects,custom-content-portfolio,1000,Custom Content Portfolio +read_private_portfolios,flash-toolkit,30000,Flash Toolkit +read_private_portfolios,suffice-toolkit,5000,Suffice Toolkit +read_private_portfolios,visual-portfolio,7000,Visual Portfolio +read_private_pricing_rates,awebooking,6000,AweBooking – Hotel Booking System +read_private_product_sets,datafeedr-product-sets,1000,Datafeedr Product Sets +read_private_products,design-approval-system,500,Design Approval System +read_private_products,easy-digital-downloads,60000,Easy Digital Downloads +read_private_products,ecommerce-product-catalog,10000,eCommerce Product Catalog Plugin for WordPress +read_private_products,gnucommerce,1000,GNUCommerce +read_private_products,jigoshop,4000,Jigoshop +read_private_products,jigoshop-ecommerce,400,Jigoshop eCommerce +read_private_products,post-type-x,1000,Product Catalog X +read_private_products,products,300,WP Products +read_private_products,webmaster-user-role,8000,Webmaster User Role +read_private_products,woocommerce,4000000,WooCommerce +read_private_products,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +read_private_profile_cct,profile-custom-content-type,200,Profile CCT +read_private_projects,upstream,1000,WordPress Project Management by UpStream +read_private_propertys,essential-real-estate,3000,Essential Real Estate +read_private_psp_project,project-panorama-lite,1000,Project Panorama +read_private_psp_projects,project-panorama-lite,1000,Project Panorama +read_private_questions,lifterlms,8000,LifterLMS +read_private_quizzes,lifterlms,8000,LifterLMS +read_private_quotes,mg-quotes,300,mg Quotes +read_private_redirects,wp-redirects,700,WP Redirects +read_private_rem_properties,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +read_private_replies,bbpress,300000,bbPress +read_private_reservations,restaurant-manager,800,Restaurant Manager +read_private_resume_positions,wp-resume,700,WP Resume +read_private_room_reservations,wp-hotelier,1000,Easy WP Hotelier +read_private_room_types,awebooking,6000,AweBooking – Hotel Booking System +read_private_rooms,wp-hotelier,1000,Easy WP Hotelier +read_private_rsvpemails,rsvpmaker,1000,RSVPMaker +read_private_rsvpmakers,rsvpmaker,1000,RSVPMaker +read_private_schedules,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_private_sgpb_popups,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +read_private_shifts,employee-scheduler,400,Shiftee Basic – Employee and Staff Scheduling +read_private_shop_coupons,jigoshop,4000,Jigoshop +read_private_shop_coupons,jigoshop-ecommerce,400,Jigoshop eCommerce +read_private_shop_coupons,webmaster-user-role,8000,Webmaster User Role +read_private_shop_coupons,woocommerce,4000000,WooCommerce +read_private_shop_discounts,easy-digital-downloads,60000,Easy Digital Downloads +read_private_shop_emails,jigoshop,4000,Jigoshop +read_private_shop_emails,jigoshop-ecommerce,400,Jigoshop eCommerce +read_private_shop_orders,jigoshop,4000,Jigoshop +read_private_shop_orders,jigoshop-ecommerce,400,Jigoshop eCommerce +read_private_shop_orders,webmaster-user-role,8000,Webmaster User Role +read_private_shop_orders,woocommerce,4000000,WooCommerce +read_private_shop_payments,easy-digital-downloads,60000,Easy Digital Downloads +read_private_shop_webhooks,woocommerce,4000000,WooCommerce +read_private_sln_attendants,salon-booking-system,5000,Salon booking system +read_private_sln_bookings,salon-booking-system,5000,Salon booking system +read_private_sln_services,salon-booking-system,5000,Salon booking system +read_private_snippets,wp-snippets,1000,WP Snippets +read_private_snitchs,snitch,1000,Snitch +read_private_sola_st_tickets,sola-support-tickets,400,Sola Support Tickets +read_private_sp_calendars,sportspress,20000,SportsPress – Sports Club & League Manager +read_private_sp_configs,sportspress,20000,SportsPress – Sports Club & League Manager +read_private_sp_events,sportspress,20000,SportsPress – Sports Club & League Manager +read_private_sp_lists,sportspress,20000,SportsPress – Sports Club & League Manager +read_private_sp_players,sportspress,20000,SportsPress – Sports Club & League Manager +read_private_sp_staffs,sportspress,20000,SportsPress – Sports Club & League Manager +read_private_sp_tables,sportspress,20000,SportsPress – Sports Club & League Manager +read_private_sp_teams,sportspress,20000,SportsPress – Sports Club & League Manager +read_private_sports,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_private_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +read_private_store_orders,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +read_private_stores,wp-store-locator,50000,WP Store Locator +read_private_sunshine_galleries,sunshine-photo-cart,2000,Sunshine Photo Cart +read_private_sunshine_orders,sunshine-photo-cart,2000,Sunshine Photo Cart +read_private_sunshine_products,sunshine-photo-cart,2000,Sunshine Photo Cart +read_private_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +read_private_tc_events,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +read_private_tc_orders,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +read_private_tc_tickets,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +read_private_tc_tickets_instances,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +read_private_teams,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_private_tm-propertys,cherry-real-estate,600,Cherry Real Estate +read_private_topics,bbpress,300000,bbPress +read_private_total_slider_slides,total-slider,500,Total Slider +read_private_trans_logs,essential-real-estate,3000,Essential Real Estate +read_private_translations,simple-punctual-translation,200,Simple Punctual Translation +read_private_tribe_events,the-events-calendar,700000,The Events Calendar +read_private_tribe_organizers,the-events-calendar,700000,The Events Calendar +read_private_tribe_venues,the-events-calendar,700000,The Events Calendar +read_private_user_packages,essential-real-estate,3000,Essential Real Estate +read_private_user_registrations,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +read_private_vacancies,job-board,300,Job Board by BestWebSoft +read_private_venues,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_private_wbcr-snippetss,insert-php,100000,PHP code snippets (Insert PHP) +read_private_wctrl_contents,widgets-control,1000,Widgets Control +read_private_wordlift_entities,wordlift,400,WordLift – AI powered SEO +read_private_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +read_private_wpcm_clubs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_private_wpcm_matchs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_private_wpcm_players,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_private_wpcm_sponsors,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_private_wpcm_staffs,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_private_wpfc_sermons,sermon-manager-for-wordpress,9000,Sermon Manager +read_private_wpi_discounts,invoicing,2000,Invoicing – Invoice & Payments Plugin +read_private_wpi_invoices,invoicing,2000,Invoicing – Invoice & Payments Plugin +read_private_wpi_items,invoicing,2000,Invoicing – Invoice & Payments Plugin +read_private_wpi_quotes,invoicing,2000,Invoicing – Invoice & Payments Plugin +read_private_wplc_quick_response,wp-live-chat-support,60000,WP Live Chat Support +read_private_wppizzas,wppizza,2000,WPPizza +read_private_wprm_reservations,wp-restaurant-manager,700,WP Restaurant Manager +read_private_wpsdealss,deals-engine,200,Social Deals Engine +read_private_wpsdealssaless,deals-engine,200,Social Deals Engine +read_private_wpse_profiles,wp-smart-editor,900,WP Smart Editor +read_private_wswebinars,wp-webinarsystem,2000,WP WebinarSystem +read_private_ycd_countdowns,countdown-builder,1000,Countdown +read_product,dc-woocommerce-multi-vendor,10000,WC Marketplace +read_product,design-approval-system,500,Design Approval System +read_product,easy-digital-downloads,60000,Easy Digital Downloads +read_product,gnucommerce,1000,GNUCommerce +read_product,jigoshop,4000,Jigoshop +read_product,jigoshop-ecommerce,400,Jigoshop eCommerce +read_product,webmaster-user-role,8000,Webmaster User Role +read_product,woocommerce,4000000,WooCommerce +read_product,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +read_product,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +read_product_set,datafeedr-product-sets,1000,Datafeedr Product Sets +read_project,upstream,1000,WordPress Project Management by UpStream +read_property,essential-real-estate,3000,Essential Real Estate +read_psp_project,project-panorama-lite,1000,Project Panorama +read_question,lifterlms,8000,LifterLMS +read_quiz,lifterlms,8000,LifterLMS +read_rem_property,real-estate-manager,1000,Real Estate Manager – Property Listing and Agent Management +read_resume_positions,wp-resume,700,WP Resume +read_room,wp-hotelier,1000,Easy WP Hotelier +read_room_reservation,wp-hotelier,1000,Easy WP Hotelier +read_room_type,awebooking,6000,AweBooking – Hotel Booking System +read_rsvpemail,rsvpmaker,1000,RSVPMaker +read_schedule,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_shift,employee-scheduler,400,Shiftee Basic – Employee and Staff Scheduling +read_shop_coupon,dc-woocommerce-multi-vendor,10000,WC Marketplace +read_shop_coupon,jigoshop,4000,Jigoshop +read_shop_coupon,jigoshop-ecommerce,400,Jigoshop eCommerce +read_shop_coupon,webmaster-user-role,8000,Webmaster User Role +read_shop_coupon,woocommerce,4000000,WooCommerce +read_shop_coupon,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +read_shop_coupons,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +read_shop_discount,easy-digital-downloads,60000,Easy Digital Downloads +read_shop_email,jigoshop,4000,Jigoshop +read_shop_email,jigoshop-ecommerce,400,Jigoshop eCommerce +read_shop_order,jigoshop,4000,Jigoshop +read_shop_order,jigoshop-ecommerce,400,Jigoshop eCommerce +read_shop_order,webmaster-user-role,8000,Webmaster User Role +read_shop_order,woocommerce,4000000,WooCommerce +read_shop_payment,easy-digital-downloads,60000,Easy Digital Downloads +read_shop_webhook,woocommerce,4000000,WooCommerce +read_shows,radio-station,1000,Radio Station +read_sln_attendant,salon-booking-system,5000,Salon booking system +read_sln_booking,salon-booking-system,5000,Salon booking system +read_sln_service,salon-booking-system,5000,Salon booking system +read_snitchs,snitch,1000,Snitch +read_sola_st_ticket,sola-support-tickets,400,Sola Support Tickets +read_sp_calendar,sportspress,20000,SportsPress – Sports Club & League Manager +read_sp_config,sportspress,20000,SportsPress – Sports Club & League Manager +read_sp_event,sportspress,20000,SportsPress – Sports Club & League Manager +read_sp_list,sportspress,20000,SportsPress – Sports Club & League Manager +read_sp_player,sportspress,20000,SportsPress – Sports Club & League Manager +read_sp_staff,sportspress,20000,SportsPress – Sports Club & League Manager +read_sp_table,sportspress,20000,SportsPress – Sports Club & League Manager +read_sp_team,sportspress,20000,SportsPress – Sports Club & League Manager +read_sport,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_stm_lms_posts,masterstudy-lms-learning-management-system,400,MasterStudy LMS – Free Learning Management System WordPress Plugin for Online Courses +read_store,wp-store-locator,50000,WP Store Locator +read_store_order,wordpress-ecommerce,3000,MarketPress – WordPress eCommerce +read_sunshine_gallery,sunshine-photo-cart,2000,Sunshine Photo Cart +read_sunshine_order,sunshine-photo-cart,2000,Sunshine Photo Cart +read_sunshine_product,sunshine-photo-cart,2000,Sunshine Photo Cart +read_support_ticket,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +read_support_tickets,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +read_tc_event,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +read_tc_order,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +read_tc_ticket,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +read_tc_tickets_instance,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +read_team,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_tm-property_feature,cherry-real-estate,600,Cherry Real Estate +read_tm-property_tag,cherry-real-estate,600,Cherry Real Estate +read_tm-property_type,cherry-real-estate,600,Cherry Real Estate +read_trans_log,essential-real-estate,3000,Essential Real Estate +read_translation,simple-punctual-translation,200,Simple Punctual Translation +read_ubn_author_notes,private-content,9000,Private Content +read_ubn_contributor_notes,private-content,9000,Private Content +read_ubn_editor_notes,private-content,9000,Private Content +read_ubn_subscriber_notes,private-content,9000,Private Content +read_user_package,essential-real-estate,3000,Essential Real Estate +read_user_registration,user-registration,7000,"User Registration – Custom Registration Form, Login and User Profile for WordPress" +read_vacancy,job-board,300,Job Board by BestWebSoft +read_venue,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +read_wbcr-snippets,insert-php,100000,PHP code snippets (Insert PHP) +read_wordlift_entity,wordlift,400,WordLift – AI powered SEO +read_wp2syslog,wp2syslog,200,wp2syslog +read_wpautoterms_pages,auto-terms-of-service-and-privacy-policy,100000,Auto Terms of Service and Privacy Policy (WP AutoTerms) +read_wpcm_club,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_wpcm_match,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_wpcm_player,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_wpcm_sponsor,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_wpcm_staff,wp-club-manager,1000,WP Club Manager – WordPress Sports Club Plugin +read_wpdiscuz_form,wpdiscuz,40000,Comments – wpDiscuz +read_wpdiscuz_forms,wpdiscuz,40000,Comments – wpDiscuz +read_wpfc_sermon,sermon-manager-for-wordpress,9000,Sermon Manager +read_wpi_discount,invoicing,2000,Invoicing – Invoice & Payments Plugin +read_wpi_invoice,invoicing,2000,Invoicing – Invoice & Payments Plugin +read_wpi_item,invoicing,2000,Invoicing – Invoice & Payments Plugin +read_wpi_quote,invoicing,2000,Invoicing – Invoice & Payments Plugin +read_wplc_quick_response,wp-live-chat-support,60000,WP Live Chat Support +read_wppizza,wppizza,2000,WPPizza +read_wprm_reservation,wp-restaurant-manager,700,WP Restaurant Manager +read_wpsdeals,deals-engine,200,Social Deals Engine +read_wpsdealssales,deals-engine,200,Social Deals Engine +read_wpse_profile,wp-smart-editor,900,WP Smart Editor +read_wswebinar,wp-webinarsystem,2000,WP WebinarSystem +reply_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +reply_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +reset_own_yop_polls_stats,yop-poll,20000,YOP Poll +reset_yop_polls_stats,yop-poll,20000,YOP Poll +restrict_content,members,100000,Members +rggcl_manage_galleries,responsive-grid-gallery-with-custom-links,2000,Responsive Grid Gallery with Custom Links +rggcl_manage_items,responsive-grid-gallery-with-custom-links,2000,Responsive Grid Gallery with Custom Links +role_prcheater,wp-postratings-cheater,2000,WP-PostRatings Cheater +rsvp_board_events,nonprofit-board-management,400,Nonprofit Board Management +run_adminer,ari-adminer,20000,ARI Adminer – WordPress Database Manager +s,yop-poll,20000,YOP Poll +sar_fsmtp_options,sar-friendly-smtp,2000,SAR Friendly SMTP +save_ticket_cap,tickera-event-ticketing-system,6000,Tickera – WordPress Event Ticketing +scfp_edit_settings,wcp-contact-form,9000,WCP Contact Form +scfp_menu,wcp-contact-form,9000,WCP Contact Form +scfp_view_inbox,wcp-contact-form,9000,WCP Contact Form +sed_edit_less,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +sed_manage_settings,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +see_all_menus,slash-admin,1000,Slash Admin +send_funds_to_user,wallets,600,Bitcoin and Altcoin Wallets +serve_as_volunteer,wired-impact-volunteer-management,1000,Wired Impact Volunteer Management +serve_on_board,nonprofit-board-management,400,Nonprofit Board Management +set_author_emd_tickets,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +set_wordpoints_points,wordpoints,700,WordPoints +setka_editor_use_editor,setka-editor,1000,Page builder for Posts – Setka Editor +settings_tickets,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +settings_tickets,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +sgpb_manage_options,popup-builder,100000,Popup Builder – Responsive WordPress Pop up – Subscription & Newsletter +shopp_capture,shopp,3000,Shopp +shopp_categories,shopp,3000,Shopp +shopp_customers,shopp,3000,Shopp +shopp_delete_customers,shopp,3000,Shopp +shopp_delete_orders,shopp,3000,Shopp +shopp_export_customers,shopp,3000,Shopp +shopp_export_orders,shopp,3000,Shopp +shopp_financials,shopp,3000,Shopp +shopp_memberships,shopp,3000,Shopp +shopp_menu,shopp,3000,Shopp +shopp_orders,shopp,3000,Shopp +shopp_products,shopp,3000,Shopp +shopp_promotions,shopp,3000,Shopp +shopp_refund,shopp,3000,Shopp +shopp_settings,shopp,3000,Shopp +shopp_settings_checkout,shopp,3000,Shopp +shopp_settings_payments,shopp,3000,Shopp +shopp_settings_presentation,shopp,3000,Shopp +shopp_settings_shipping,shopp,3000,Shopp +shopp_settings_system,shopp,3000,Shopp +shopp_settings_taxes,shopp,3000,Shopp +shopp_settings_update,shopp,3000,Shopp +shopp_void,shopp,3000,Shopp +si_delete_profile,simple-intranet-directory,600,Simple Intranet Directory +si_delete_profiles,simple-intranet-directory,600,Simple Intranet Directory +si_edit_outofoffice,simple-intranet-directory,600,Simple Intranet Directory +si_edit_profile,simple-intranet-directory,600,Simple Intranet Directory +si_edit_profiles,simple-intranet-directory,600,Simple Intranet Directory +si_publish_profile,simple-intranet-directory,600,Simple Intranet Directory +si_read_profile,simple-intranet-directory,600,Simple Intranet Directory +simple_tags,simple-tags,100000,Simple Tags +site_editor_manage,site-editor,300,Site Editor – WordPress Site Builder – Theme Builder and Page Builder +slideshow-jquery-image-gallery-add-slideshows,slideshow-jquery-image-gallery,100000,Slideshow +slideshow-jquery-image-gallery-delete-slideshows,slideshow-jquery-image-gallery,100000,Slideshow +slideshow-jquery-image-gallery-edit-slideshows,slideshow-jquery-image-gallery,100000,Slideshow +slideshow_about,slideshow-gallery,20000,Slideshow Gallery +slideshow_galleries,slideshow-gallery,20000,Slideshow Gallery +slideshow_settings,slideshow-gallery,20000,Slideshow Gallery +slideshow_slides,slideshow-gallery,20000,Slideshow Gallery +slideshow_submitserial,slideshow-gallery,20000,Slideshow Gallery +slideshow_welcome,slideshow-gallery,20000,Slideshow Gallery +smartslider,smart-slider-3,300000,Smart Slider 3 +smartslider_config,smart-slider-3,300000,Smart Slider 3 +smartslider_delete,smart-slider-3,300000,Smart Slider 3 +smartslider_edit,smart-slider-3,300000,Smart Slider 3 +snowball_delete_posts,snowball,500,Snowball +snowball_edit_others_posts,snowball,500,Snowball +snowball_edit_posts,snowball,500,Snowball +snowball_publish_posts,snowball,500,Snowball +snowball_read_private_posts,snowball,500,Snowball +sp_cdm,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_categories,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_forms,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_groups,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_help,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_link,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_local_import,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_media,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_projects,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_settings,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_show_folders_as_nav,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_top_menu,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_uploader,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_user_logs,sp-client-document-manager,3000,SP Project & Document Manager +sp_cdm_vendors,sp-client-document-manager,3000,SP Project & Document Manager +spectate,bbpress,300000,bbPress +srm_manage_redirects,safe-redirect-manager,40000,Safe Redirect Manager +status_change_assigned,publishpress,1000,PublishPress – Professional publishing tools for WordPress +status_change_draft,publishpress,1000,PublishPress – Professional publishing tools for WordPress +status_change_future,publishpress,1000,PublishPress – Professional publishing tools for WordPress +status_change_in_progress,publishpress,1000,PublishPress – Professional publishing tools for WordPress +status_change_pending,publishpress,1000,PublishPress – Professional publishing tools for WordPress +status_change_pitch,publishpress,1000,PublishPress – Professional publishing tools for WordPress +status_change_private,publishpress,1000,PublishPress – Professional publishing tools for WordPress +status_change_publish,publishpress,1000,PublishPress – Professional publishing tools for WordPress +strong_testimonials_about,strong-testimonials,60000,Strong Testimonials +strong_testimonials_fields,strong-testimonials,60000,Strong Testimonials +strong_testimonials_options,strong-testimonials,60000,Strong Testimonials +strong_testimonials_views,strong-testimonials,60000,Strong Testimonials +sunshine_manage_options,sunshine-photo-cart,2000,Sunshine Photo Cart +swifty_change_lock,swifty-content-creator,1000,Swifty Content Creator +swifty_change_lock,swifty-page-manager,5000,Swifty Page Manager +swifty_change_lock,swifty-site,1000,SwiftySite +swifty_edit_locked,swifty-content-creator,1000,Swifty Content Creator +swifty_edit_locked,swifty-page-manager,5000,Swifty Page Manager +swifty_edit_locked,swifty-site,1000,SwiftySite +switch_ai1ec_themes,all-in-one-event-calendar,100000,All-in-One Event Calendar +tablepress_access_about_screen,tablepress,700000,TablePress +tablepress_access_about_screen,webmaster-user-role,8000,Webmaster User Role +tablepress_access_options_screen,tablepress,700000,TablePress +tablepress_access_options_screen,webmaster-user-role,8000,Webmaster User Role +tablepress_add_tables,tablepress,700000,TablePress +tablepress_add_tables,webmaster-user-role,8000,Webmaster User Role +tablepress_copy_tables,tablepress,700000,TablePress +tablepress_delete_tables,tablepress,700000,TablePress +tablepress_edit_options,tablepress,700000,TablePress +tablepress_edit_tables,tablepress,700000,TablePress +tablepress_edit_tables,webmaster-user-role,8000,Webmaster User Role +tablepress_export_tables,tablepress,700000,TablePress +tablepress_export_tables,webmaster-user-role,8000,Webmaster User Role +tablepress_import_tables,tablepress,700000,TablePress +tablepress_import_tables,webmaster-user-role,8000,Webmaster User Role +tablepress_import_tables_wptr,tablepress,700000,TablePress +tablepress_list_tables,tablepress,700000,TablePress +tablepress_list_tables,webmaster-user-role,8000,Webmaster User Role +task_assigned_to_field,upstream,1000,WordPress Project Management by UpStream +task_end_date_field,upstream,1000,WordPress Project Management by UpStream +task_milestone_field,upstream,1000,WordPress Project Management by UpStream +task_notes_field,upstream,1000,WordPress Project Management by UpStream +task_progress_field,upstream,1000,WordPress Project Management by UpStream +task_start_date_field,upstream,1000,WordPress Project Management by UpStream +task_status_field,upstream,1000,WordPress Project Management by UpStream +task_title_field,upstream,1000,WordPress Project Management by UpStream +tcp_checkout_editor,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_delete_product,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_downloadable_products,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_address,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_addresses,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_orders,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_others_products,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_plugins,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_product,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_products,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_settings,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_taxes,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_edit_wish_list,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_publish_products,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_read_orders,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_read_product,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_shortcode_generator,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_update_price,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_update_stock,thecartpress,1000,TheCartPress eCommerce Shopping Cart +tcp_users_roles,thecartpress,1000,TheCartPress eCommerce Shopping Cart +throttle,bbpress,300000,bbPress +ticket_delete_channels,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_delete_departments,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_delete_priorities,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_delete_products,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_delete_tags,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_edit_channels,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_edit_departments,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_edit_priorities,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_edit_products,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_edit_tags,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_manage_channels,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_manage_departments,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_manage_priorities,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_manage_privacy,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_manage_products,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_manage_tags,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +ticket_taxonomy,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +total_slider_manage_slides,total-slider,500,Total Slider +track_cforms,cforms2,10000,cformsII +trackserver_admin,trackserver,500,Trackserver +trackserver_publish,trackserver,500,Trackserver +unenroll,lifterlms,8000,LifterLMS +update all intel visitors,intelligence,600,Intelligence +update_plugin,wp2appir,300,wp2appir +update_results,leaguemanager,2000,LeagueManager +update_wordpoints_extensions,wordpoints,700,WordPoints +upload_event_images,events-manager,100000,Events Manager +upload_lazyest_file,lazyest-gallery,1000,Lazyest Gallery +upload_media,classifieds-wp,800,Classifieds WP +upstream_comment_images,upstream,1000,WordPress Project Management by UpStream +ure_create_capabilities,user-role-editor,500000,User Role Editor +ure_create_roles,user-role-editor,500000,User Role Editor +ure_delete_capabilities,user-role-editor,500000,User Role Editor +ure_delete_roles,user-role-editor,500000,User Role Editor +ure_edit_roles,user-role-editor,500000,User Role Editor +ure_manage_options,user-role-editor,500000,User Role Editor +ure_reset_roles,user-role-editor,500000,User Role Editor +use-wp-users-exporter,wp-users-exporter,2000,WP Users Exporter +use_copyscape,copyscape-premium,1000,Copyscape Premium +use_openid_provider,openid,3000,OpenID +use_support,ucare-support-system,3000,uCare – Support Ticket System & HelpDesk +use_teachpress,teachpress,1000,teachPress +use_teachpress_courses,teachpress,1000,teachPress +use_trackserver,trackserver,500,Trackserver +use_wp_admin_microblog,wp-admin-microblog,200,WP Admin Microblog +use_wp_admin_microblog_bp,wp-admin-microblog,200,WP Admin Microblog +use_wp_admin_microblog_sticky,wp-admin-microblog,200,WP Admin Microblog +user_meta_admin,user-meta,3000,User Meta – User Profile Builder and User management plugin +vc_access_rules_post_types/enhancedcategory,enhanced-category-pages,5000,Enhanced Category Pages +view all intel emailclicks,intelligence,600,Intelligence +view all intel phonecalls,intelligence,600,Intelligence +view all intel reports,intelligence,600,Intelligence +view all intel submissions,intelligence,600,Intelligence +view all intel visitors,intelligence,600,Intelligence +view own intel reports,intelligence,600,Intelligence +view-customer-area-menu,customer-area,10000,WP Customer Area +view_admin_dashboard,weblibrarian,700,WebLibrarian +view_admin_dashboard,wsdesk,1000,WSDesk – WordPress HelpDesk & Support Ticket System +view_admin_dashboard,apply-online,5000,Apply Online +view_all_aryo_activity_log,aryo-activity-log,90000,Activity Log +view_all_tickets,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +view_board_content,nonprofit-board-management,400,Nonprofit Board Management +view_campus_directory_dashboard,campus-directory,200,Faculty Staff and Student Directory Plugin – Campus Directory +view_charitable_sensitive_data,charitable,10000,Charitable – Donation Plugin +view_cimy_extra_fields,cimy-user-extra-fields,10000,Cimy User Extra Fields +view_contact_manager,contact-manager,500,Contact Manager +view_developer_content,developer-mode,1000,Developer Mode +view_developer_menu_items,developer-mode,1000,Developer Mode +view_developer_plugins,developer-mode,1000,Developer Mode +view_directory,pta-member-directory,1000,PTA Member Directory and Contact Form +view_empd_com_dashboard,employee-directory,400,Staff Directory – Employee Directory for WordPress +view_employee_spotlight_dashboard,employee-spotlight,1000,Team Members Staff Showcase Plugin – Employee Spotlight +view_errors,timber,400,Timber +view_give_form_stats,give,50000,Give – Donation Plugin and Fundraising Platform +view_give_payment_stats,give,50000,Give – Donation Plugin and Fundraising Platform +view_give_payments,give,50000,Give – Donation Plugin and Fundraising Platform +view_give_reports,give,50000,Give – Donation Plugin and Fundraising Platform +view_give_sensitive_data,give,50000,Give – Donation Plugin and Fundraising Platform +view_h5p_results,h5p,10000,Interactive Content – H5P +view_jigoshop_reports,jigoshop,4000,Jigoshop +view_jigoshop_reports,jigoshop-ecommerce,400,Jigoshop eCommerce +view_leagues,leaguemanager,2000,LeagueManager +view_lifterlms_reports,lifterlms,8000,LifterLMS +view_mp_menu_item_stats,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +view_mprm_order_stats,mp-restaurant-menu,6000,Restaurant Menu by MotoPress +view_mstw_menus,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +view_mstw_menus,team-rosters,800,Team Rosters +view_mstw_ss_menus,mstw-schedules-scoreboards,500,MSTW Schedules & Scoreboards +view_mstw_tr_menus,team-rosters,800,Team Rosters +view_opalestate_agents_stats,opal-estate,1000,Opal Estate +view_opalestate_properties_stats,opal-estate,1000,Opal Estate +view_opalestate_reports,opal-estate,1000,Opal Estate +view_opalestate_sensitive_data,opal-estate,1000,Opal Estate +view_others_lifterlms_reports,lifterlms,8000,LifterLMS +view_others_posts,wp-admin-hide-others-posts,300,WP Admin Hide Other's Posts +view_own_yop_polls_logs,yop-poll,20000,YOP Poll +view_own_yop_polls_results,yop-poll,20000,YOP Poll +view_pdf,projectmanager,400,ProjectManager +view_private_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +view_product_stats,easy-digital-downloads,60000,Easy Digital Downloads +view_projects,projectmanager,400,ProjectManager +view_query_monitor,query-monitor,50000,Query Monitor +view_recent_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +view_recent_dash_contacts,wp-easy-contact,600,Best Contact Management Software for WordPress +view_recent_tickets_dashboard,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +view_request_a_quote_dashboard,request-a-quote,1000,Request a Quote +view_shop_discount_stats,easy-digital-downloads,60000,Easy Digital Downloads +view_shop_payment_stats,easy-digital-downloads,60000,Easy Digital Downloads +view_shop_reports,easy-digital-downloads,60000,Easy Digital Downloads +view_shop_sensitive_data,easy-digital-downloads,60000,Easy Digital Downloads +view_single_quote,request-a-quote,1000,Request a Quote +view_sportspress_reports,sportspress,20000,SportsPress – Sports Club & League Manager +view_sprout_invoices_dashboard,sprout-invoices,2000,Client Invoicing by Sprout Invoices – Easy Estimates and Invoices for WordPress +view_template_name,display-template-name,2000,Display Template Name +view_ticket,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +view_ticket,catchers-helpdesk,200,Catchers Helpdesk and Ticket system for Support +view_trash,bbpress,300000,bbPress +view_unassigned_tickets,awesome-support,9000,Awesome Support – WordPress HelpDesk & Support Plugin +view_wallets_profile,wallets,600,Bitcoin and Altcoin Wallets +view_woocommerce_reports,dc-woocommerce-multi-vendor,10000,WC Marketplace +view_woocommerce_reports,webmaster-user-role,8000,Webmaster User Role +view_woocommerce_reports,woocommerce,4000000,WooCommerce +view_woocommerce_reports,wc-multivendor-marketplace,1000,WooCommerce Multivendor Marketplace +view_wp_easy_contact_dashboard,wp-easy-contact,600,Best Contact Management Software for WordPress +view_wp_template_viewer,wp-template-viewer,200,WP Template Viewer +view_wp_ticket_com_dashboard,wp-ticket,400,Best Customer Customer Service Software & Support Ticket System for WordPress +view_wptao_reports,wp-tao,2000,"Track, Analyze & Optimize by WP Tao" +view_yop_polls_imports,yop-poll,20000,YOP Poll +view_yop_polls_logs,yop-poll,20000,YOP Poll +view_yop_polls_results,yop-poll,20000,YOP Poll +view_youtube_showcase_dashboard,youtube-showcase,7000,YouTube Gallery – Best YouTube Video Gallery for WordPress +vote_on_comments,comment-popularity,300,Comment Popularity +wcjp_addcss_code,custom-css-js-php,7000,Custom css-js-php +wcjp_addjs_code,custom-css-js-php,7000,Custom css-js-php +wcjp_addphp_code,custom-css-js-php,7000,Custom css-js-php +wcjp_admin_overview,custom-css-js-php,7000,Custom css-js-php +wcjp_how_overview,custom-css-js-php,7000,Custom css-js-php +wcjp_managecss_code,custom-css-js-php,7000,Custom css-js-php +wcjp_managejs_code,custom-css-js-php,7000,Custom css-js-php +wcjp_managephp_code,custom-css-js-php,7000,Custom css-js-php +wcup_manager,world-cup-predictor,400,World Cup Predictor +wh_admin_overview,word-highlighter,500,Word Highlighter +wh_how_overview,word-highlighter,500,Word Highlighter +wh_manage_settings,word-highlighter,500,Word Highlighter +wholesale,pricing-deals-for-woocommerce,7000,Pricing Deals for WooCommerce +withdraw_funds_from_wallet,wallets,600,Bitcoin and Altcoin Wallets +wop_admin_overview,wp-overlays,2000,WP Overlays +wop_how_overview,wp-overlays,2000,WP Overlays +wop_manage_settings,wp-overlays,2000,WP Overlays +wp-piwik_read_stats,wp-piwik,70000,WP-Matomo (WP-Piwik) +wpProQuiz_add_quiz,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_change_settings,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_delete_quiz,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_edit_quiz,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_export,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_import,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_reset_statistics,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_show,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_show_statistics,wp-pro-quiz,20000,Wp-Pro-Quiz +wpProQuiz_toplist_edit,wp-pro-quiz,20000,Wp-Pro-Quiz +wp_power_stats_configure,wp-power-stats,10000,WP Power Stats +wp_power_stats_view,wp-power-stats,10000,WP Power Stats +wp_review_description,wp-review,80000,WP Review +wp_review_features,wp-review,80000,WP Review +wp_review_global_options,wp-review,80000,WP Review +wp_review_import_reviews,wp-review,80000,WP Review +wp_review_links,wp-review,80000,WP Review +wp_review_notification_bar,wp-review,80000,WP Review +wp_review_purge_comment_ratings,wp-review,80000,WP Review +wp_review_purge_visitor_ratings,wp-review,80000,WP Review +wp_review_single_page,wp-review,80000,WP Review +wp_review_user_reviews,wp-review,80000,WP Review +wp_show_stats_visibility,wp-show-stats,2000,WP Show Stats +wpaa_set_comment_cap,wp-access-areas,1000,WordPress Access Areas +wpaa_set_edit_cap,wp-access-areas,1000,WordPress Access Areas +wpaa_set_view_cap,wp-access-areas,1000,WordPress Access Areas +wpam_admin,affiliates-manager,10000,Affiliates Manager +wpcf_custom_field_edit,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_custom_field_edit_others,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_custom_field_view,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_custom_post_type_edit,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_custom_post_type_edit_others,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_custom_post_type_view,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_custom_taxonomy_edit,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_custom_taxonomy_edit_others,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_custom_taxonomy_view,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_user_meta_field_edit,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_user_meta_field_edit_others,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpcf_user_meta_field_view,types,200000,"Toolset Types – Custom Post Types, Custom Fields and Taxonomies" +wpe_admin_overview,wp-prayer,800,WP Prayer +wpe_form_prayer,wp-prayer,800,WP Prayer +wpe_manage_email_settings,wp-prayer,800,WP Prayer +wpe_manage_prayer,wp-prayer,800,WP Prayer +wpe_manage_prayers_performed,wp-prayer,800,WP Prayer +wpe_manage_settings,wp-prayer,800,WP Prayer +wpe_prayers_export,wp-prayer,800,WP Prayer +wpfd_create_category,wp-smart-editor,900,WP Smart Editor +wpfd_delete_category,wp-smart-editor,900,WP Smart Editor +wpfd_edit_category,wp-smart-editor,900,WP Smart Editor +wpfd_edit_own_category,wp-smart-editor,900,WP Smart Editor +wpfd_manage_file,wp-smart-editor,900,WP Smart Editor +wpgmp_admin_overview,wp-google-map-plugin,100000,WP Google Map Plugin +wpgmp_form_group_map,wp-google-map-plugin,100000,WP Google Map Plugin +wpgmp_form_location,wp-google-map-plugin,100000,WP Google Map Plugin +wpgmp_form_map,wp-google-map-plugin,100000,WP Google Map Plugin +wpgmp_how_overview,wp-google-map-plugin,100000,WP Google Map Plugin +wpgmp_manage_group_map,wp-google-map-plugin,100000,WP Google Map Plugin +wpgmp_manage_location,wp-google-map-plugin,100000,WP Google Map Plugin +wpgmp_manage_map,wp-google-map-plugin,100000,WP Google Map Plugin +wpgmp_manage_settings,wp-google-map-plugin,100000,WP Google Map Plugin +wphr_create_document,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_create_employee,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_create_review,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_delete_document,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_delete_employee,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_delete_review,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_edit_document,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_edit_employee,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_leave_create_request,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_leave_mails,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_leave_manage,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_list_employee,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_manage_announcement,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_manage_department,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_manage_designation,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_manage_hr_settings,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_manage_jobinfo,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_manage_review,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_view_document,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_view_employee,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wphr_view_jobinfo,wp-hr-manager,300,WP-HR Manager: The Human Resources Plugin for WordPress +wplc_ma_agent,wp-live-chat-support,60000,WP Live Chat Support +wpml_manage_woocommerce_multilingual,woocommerce-multilingual,90000,WooCommerce Multilingual – run WooCommerce with WPML +wpml_operate_woocommerce_multilingual,woocommerce-multilingual,90000,WooCommerce Multilingual – run WooCommerce with WPML +wpp_admin_overview,wp-posts-master,1000,WP Posts Master +wpp_form_layout,wp-posts-master,1000,WP Posts Master +wpp_form_rules,wp-posts-master,1000,WP Posts Master +wpp_how_overview,wp-posts-master,1000,WP Posts Master +wpp_manage_layout,wp-posts-master,1000,WP Posts Master +wpp_manage_rules,wp-posts-master,1000,WP Posts Master +wppa_admin,wp-photo-album-plus,30000,WP Photo Album Plus +wppa_comments,wp-photo-album-plus,30000,WP Photo Album Plus +wppa_export,wp-photo-album-plus,30000,WP Photo Album Plus +wppa_help,wp-photo-album-plus,30000,WP Photo Album Plus +wppa_import,wp-photo-album-plus,30000,WP Photo Album Plus +wppa_moderate,wp-photo-album-plus,30000,WP Photo Album Plus +wppa_potd,wp-photo-album-plus,30000,WP Photo Album Plus +wppa_settings,wp-photo-album-plus,30000,WP Photo Album Plus +wppa_upload,wp-photo-album-plus,30000,WP Photo Album Plus +wppcp_manage_options,wp-private-content-plus,7000,WP Private Content Plus +wppizza_cap_access,wppizza,2000,WPPizza +wppizza_cap_access_rights,wppizza,2000,WPPizza +wppizza_cap_additives,wppizza,2000,WPPizza +wppizza_cap_categories,wppizza,2000,WPPizza +wppizza_cap_customers,wppizza,2000,WPPizza +wppizza_cap_delete_order,wppizza,2000,WPPizza +wppizza_cap_gateways,wppizza,2000,WPPizza +wppizza_cap_layout,wppizza,2000,WPPizza +wppizza_cap_localization,wppizza,2000,WPPizza +wppizza_cap_meal_sizes,wppizza,2000,WPPizza +wppizza_cap_menu_items,wppizza,2000,WPPizza +wppizza_cap_opening_times,wppizza,2000,WPPizza +wppizza_cap_openingtimes,wppizza,2000,WPPizza +wppizza_cap_order_form,wppizza,2000,WPPizza +wppizza_cap_order_form_settings,wppizza,2000,WPPizza +wppizza_cap_order_history,wppizza,2000,WPPizza +wppizza_cap_order_settings,wppizza,2000,WPPizza +wppizza_cap_orderhistory,wppizza,2000,WPPizza +wppizza_cap_reports,wppizza,2000,WPPizza +wppizza_cap_settings,wppizza,2000,WPPizza +wppizza_cap_templates,wppizza,2000,WPPizza +wppizza_cap_tools,wppizza,2000,WPPizza +wprc_add_new_capability,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_add_new_role,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_change_default_role,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_delete_role,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_export_role_caps,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_import_role_caps,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_manage_all_capabilities,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_manage_user_capabilities,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_remove_capability,user-roles-and-capabilities,10000,User Roles and Capabilities +wprc_rename_role,user-roles-and-capabilities,10000,User Roles and Capabilities +wpseo_bulk_edit,wordpress-seo,5000000,Yoast SEO +wpseo_edit_advanced_metadata,wordpress-seo,5000000,Yoast SEO +wpseo_manage_options,wordpress-seo,5000000,Yoast SEO +wpsg_conf,wpshopgermany-free,400,wpShopGermany Free +wpsg_lizence,wpshopgermany-free,400,wpShopGermany Free +wpsg_menu,wpshopgermany-free,400,wpShopGermany Free +wpsg_order,wpshopgermany-free,400,wpShopGermany Free +wpsg_produkt,wpshopgermany-free,400,wpShopGermany Free +wpshop_add_attribute_group,wpshop,1000,=== WPshop – eCommerce +wpshop_add_attribute_set,wpshop,1000,=== WPshop – eCommerce +wpshop_add_attributes,wpshop,1000,=== WPshop – eCommerce +wpshop_add_attributes_select_values,wpshop,1000,=== WPshop – eCommerce +wpshop_add_attributes_unit,wpshop,1000,=== WPshop – eCommerce +wpshop_add_attributes_unit_group,wpshop,1000,=== WPshop – eCommerce +wpshop_add_product,wpshop,1000,=== WPshop – eCommerce +wpshop_delete_attribute_group,wpshop,1000,=== WPshop – eCommerce +wpshop_delete_attribute_set,wpshop,1000,=== WPshop – eCommerce +wpshop_delete_attributes,wpshop,1000,=== WPshop – eCommerce +wpshop_delete_attributes_select_values,wpshop,1000,=== WPshop – eCommerce +wpshop_delete_attributes_unit,wpshop,1000,=== WPshop – eCommerce +wpshop_delete_attributes_unit_group,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_advanced_options,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_attribute_group,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_attribute_group_details,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_attribute_set,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_attributes,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_attributes_select_values,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_attributes_unit,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_attributes_unit_group,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_options,wpshop,1000,=== WPshop – eCommerce +wpshop_edit_product,wpshop,1000,=== WPshop – eCommerce +wpshop_manage_product_categories,wpshop,1000,=== WPshop – eCommerce +wpshop_view_addons,wpshop,1000,=== WPshop – eCommerce +wpshop_view_advanced_options,wpshop,1000,=== WPshop – eCommerce +wpshop_view_attribute_group,wpshop,1000,=== WPshop – eCommerce +wpshop_view_attribute_group_details,wpshop,1000,=== WPshop – eCommerce +wpshop_view_attribute_set,wpshop,1000,=== WPshop – eCommerce +wpshop_view_attribute_set_details,wpshop,1000,=== WPshop – eCommerce +wpshop_view_attributes,wpshop,1000,=== WPshop – eCommerce +wpshop_view_attributes_unit,wpshop,1000,=== WPshop – eCommerce +wpshop_view_attributes_unit_group,wpshop,1000,=== WPshop – eCommerce +wpshop_view_coupons,wpshop,1000,=== WPshop – eCommerce +wpshop_view_dashboard,wpshop,1000,=== WPshop – eCommerce +wpshop_view_documentation_menu,wpshop,1000,=== WPshop – eCommerce +wpshop_view_groups,wpshop,1000,=== WPshop – eCommerce +wpshop_view_import_menu,wpshop,1000,=== WPshop – eCommerce +wpshop_view_messages,wpshop,1000,=== WPshop – eCommerce +wpshop_view_options,wpshop,1000,=== WPshop – eCommerce +wpshop_view_orders,wpshop,1000,=== WPshop – eCommerce +wpshop_view_product,wpshop,1000,=== WPshop – eCommerce +wpshop_view_shortcodes,wpshop,1000,=== WPshop – eCommerce +wpshop_view_statistics,wpshop,1000,=== WPshop – eCommerce +wpshop_view_tools_menu,wpshop,1000,=== WPshop – eCommerce +wpsms_outbox,wp-sms,7000,WP SMS +wpsms_sendsms,wp-sms,7000,WP SMS +wpsms_setting,wp-sms,7000,WP SMS +wpsms_subscribe_groups,wp-sms,7000,WP SMS +wpsms_subscribers,wp-sms,7000,WP SMS +wpsqt-manage,wp-survey-and-quiz-tool,4000,WP Survey And Quiz Tool +wpt_can_tweet,wp-to-twitter,60000,WP to Twitter +wpt_tweet_now,wp-to-twitter,60000,WP to Twitter +wpt_twitter_custom,wp-to-twitter,60000,WP to Twitter +wpt_twitter_oauth,wp-to-twitter,60000,WP to Twitter +wpt_twitter_switch,wp-to-twitter,60000,WP to Twitter +write_reply_hanaboard-post,hana-board,1000,Hana-Board 하나보드 워드프레스 게시판 +wsq_admin_overview,wp-security-questions,1000,WP Security Question +wsq_how_overview,wp-security-questions,1000,WP Security Question +wsq_manage_settings,wp-security-questions,1000,WP Security Question +wth_admin_overview,was-this-helpful,500,Was This Helpful +wth_form_helpful,was-this-helpful,500,Was This Helpful +wth_how_overview,was-this-helpful,500,Was This Helpful +wysija_config,wysija-newsletters,300000,MailPoet Newsletters (Previous) +wysija_newsletters,wysija-newsletters,300000,MailPoet Newsletters (Previous) +wysija_stats_dashboard,wysija-newsletters,300000,MailPoet Newsletters (Previous) +wysija_style_tab,wysija-newsletters,300000,MailPoet Newsletters (Previous) +wysija_subscribers,wysija-newsletters,300000,MailPoet Newsletters (Previous) +wysija_theme_tab,wysija-newsletters,300000,MailPoet Newsletters (Previous) +xili_dictionary_admin,xili-dictionary,800,xili-dictionary +xili_dictionary_edit,xili-dictionary,800,xili-dictionary +xili_dictionary_edit_save,xili-dictionary,800,xili-dictionary +xili_language_clone_tax,xili-language,3000,xili-language +xili_language_menu,xili-language,3000,xili-language +xili_language_set,xili-language,3000,xili-language +xili_tidy_admin_set,xili-tidy-tags,1000,xili-tidy-tags +xili_tidy_editor_group,xili-tidy-tags,1000,xili-tidy-tags +xili_tidy_editor_set,xili-tidy-tags,1000,xili-tidy-tags +ycd_manage_options,countdown-builder,1000,Countdown +yop_poll_add,yop-poll,20000,YOP Poll +yop_poll_delete_others,yop-poll,20000,YOP Poll +yop_poll_delete_own,yop-poll,20000,YOP Poll +yop_poll_edit_others,yop-poll,20000,YOP Poll +yop_poll_edit_own,yop-poll,20000,YOP Poll +yop_poll_results_others,yop-poll,20000,YOP Poll +yop_poll_results_own,yop-poll,20000,YOP Poll +zbs_dash,zero-bs-crm,1000,Zero BS WordPress CRM \ No newline at end of file diff --git a/extras/modules/role-editor/data/single-site-admin-caps.txt b/extras/modules/role-editor/data/single-site-admin-caps.txt new file mode 100644 index 0000000..7087ce7 --- /dev/null +++ b/extras/modules/role-editor/data/single-site-admin-caps.txt @@ -0,0 +1,18 @@ +// Only Administrators of single site installations have the following capabilities. +// In Multisite, only the Super Admin has these abilities. +// See: http://codex.wordpress.org/Roles_and_Capabilities#Additional_Admin_Capabilities + +update_core +update_plugins +update_themes +install_plugins +install_themes +delete_themes +delete_plugins +edit_plugins +edit_themes +edit_files +edit_users +create_users +delete_users +unfiltered_html \ No newline at end of file diff --git a/extras/modules/role-editor/data/stable-meta-caps.txt b/extras/modules/role-editor/data/stable-meta-caps.txt new file mode 100644 index 0000000..44b74a4 --- /dev/null +++ b/extras/modules/role-editor/data/stable-meta-caps.txt @@ -0,0 +1,26 @@ +// These are known meta capabilities that each reliably map to a single capability if they are enabled. +// Meta capabilities that have context-dependent mappings are not included in this list. + +promote_user +add_users +edit_css +upload_themes +upload_plugins +update_languages +deactivate_plugins +customize +delete_site + +edit_categories +delete_categories +assign_categories + +manage_post_tags +edit_post_tags +delete_post_tags +assign_post_tags + +update_php +export_others_personal_data +erase_others_personal_data +manage_privacy_options \ No newline at end of file diff --git a/extras/modules/role-editor/data/superadmin-only-caps.txt b/extras/modules/role-editor/data/superadmin-only-caps.txt new file mode 100644 index 0000000..02895d4 --- /dev/null +++ b/extras/modules/role-editor/data/superadmin-only-caps.txt @@ -0,0 +1,8 @@ +//These capabilities are only available to Multisite Super Admins: + +manage_network +manage_sites +manage_network_users +manage_network_plugins +manage_network_themes +manage_network_options \ No newline at end of file diff --git a/extras/modules/role-editor/data/update-cap-db.php b/extras/modules/role-editor/data/update-cap-db.php new file mode 100644 index 0000000..ccf8e22 --- /dev/null +++ b/extras/modules/role-editor/data/update-cap-db.php @@ -0,0 +1,187 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$pdo->exec("ATTACH DATABASE '$inputFileName' AS smokebase"); +$pdo->exec("PRAGMA foreign_keys = ON"); + +$statement = $pdo->query('SELECT COUNT(*) FROM smokebase.plugins'); +printf( + "%d plugins available\n", + $statement->fetchColumn(0) +); +$statement->closeCursor(); + +//Insert new plugins. +echo "Inserting new plugins\n"; +$pdo->exec(" + INSERT INTO components(typeId, slug, name, activeInstalls) + SELECT + componentTypes.typeId, plugins.slug, coalesce(plugins.name_header, plugins.name), plugins.active_installs + FROM + plugins + JOIN componentTypes + WHERE componentTypes.prefix = 'plugin:' + ON CONFLICT DO NOTHING +"); + +//Update plugin names and install stats. +echo "Updating plugin names\n"; +$pdo->exec(" + UPDATE components + SET name = coalesce(( + SELECT coalesce(plugins.name_header, plugins.name) + FROM plugins + WHERE plugins.slug = components.slug + ), name) + WHERE components.typeId = (SELECT componentTypes.typeId FROM componentTypes WHERE componentTypes.prefix = 'plugin:') +"); + +echo "Updating active installs\n"; +$pdo->exec(" + UPDATE components + SET activeInstalls = coalesce(( + SELECT plugins.active_installs + FROM plugins + WHERE plugins.slug = components.slug + ), activeInstalls) + WHERE components.typeId = (SELECT componentTypes.typeId FROM componentTypes WHERE componentTypes.prefix = 'plugin:') +"); + +//Insert new capabilities. +echo "Inserting new capabilities\n"; + +/** @noinspection SqlConstantCondition */ +$pdo->exec(" + INSERT INTO capabilities(name) + SELECT smokebase.capabilities.name + FROM smokebase.capabilities + WHERE 1=1 + ON CONFLICT DO NOTHING +"); + +echo "Updating plugin-capability relationships\n"; +$pdo->exec(" + INSERT INTO componentCapabilityInfo(capabilityId, numericId) + SELECT DISTINCT + capabilities.capabilityId, components.numericId + FROM + plugin_capabilities + JOIN smokebase.capabilities ON (smokebase.capabilities.entity_id = plugin_capabilities.entity_id) + JOIN reports ON (plugin_capabilities.report_id = reports.report_id) + JOIN plugins ON (plugins.slug = reports.slug) + JOIN components ON (components.slug = plugins.slug) + JOIN componentTypes ON (componentTypes.typeId = components.typeId) + JOIN main.capabilities ON (main.capabilities.name = smokebase.capabilities.name) + WHERE + componentTypes.prefix = 'plugin:' + ON CONFLICT DO NOTHING +"); + +$pdo = null; + +//Make a copy of the database. +$tempFileName = tempnam(sys_get_temp_dir(), 'rex'); +printf("Creating temporary file %s\n", $tempFileName); +copy($outputFileName, $tempFileName); + +$excerptDb = new PDO('sqlite:' . $tempFileName); +$excerptDb->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$excerptDb->exec("PRAGMA foreign_keys = OFF"); + +//Delete plugins that don't have any associated capabilities. +echo "Deleting plugins without any capabilities (this can take a while)\n"; + +$excerptDb->exec(" + DELETE FROM components + WHERE components.numericId IN ( + select a.numericId + from components AS a LEFT JOIN componentCapabilityInfo ON (a.numericId = componentCapabilityInfo.numericId) + WHERE componentCapabilityInfo.capabilityId is NULL + ) + AND components.typeId = (SELECT typeId from componentTypes WHERE prefix = 'plugin:') +"); + +//Delete plugins with less than X installs. +echo "Deleting unpopular plugins\n"; +$excerptDb->exec("PRAGMA foreign_keys = ON"); +$excerptDb->exec(" + DELETE FROM components + WHERE activeInstalls < 100 + AND components.typeId = (SELECT typeId from componentTypes WHERE prefix = 'plugin:') +"); + +echo "Deleting unused capabilities\n"; +$excerptDb->exec(" + DELETE FROM capabilities + WHERE capabilityId IN ( + SELECT capabilities.capabilityId + FROM capabilities LEFT JOIN componentCapabilityInfo + ON (capabilities.capabilityId = componentCapabilityInfo.capabilityId) + WHERE componentCapabilityInfo.numericId IS NULL + ) +"); + +//The database probably has some empty space after deleting all of that data, +//so lets compact it to reduce its size. +$excerptDb->exec("VACUUM"); + +$excerptDb = null; +$size = filesize($tempFileName); +printf("Excerpt size: %d bytes\n", $size); + +//Move the file to the appropriate location. +if ( copy($tempFileName, $excerptFileName) ) { + printf("File moved to: %s\n", $excerptFileName); +} else { + printf("Error: Could not move the file to %s", $excerptFileName); + exit(10); +} +unlink($tempFileName); + +printf("Elapsed time: %.3f seconds\n", microtime(true) - $startTime); \ No newline at end of file diff --git a/extras/modules/role-editor/load.php b/extras/modules/role-editor/load.php new file mode 100644 index 0000000..ba9b8fa --- /dev/null +++ b/extras/modules/role-editor/load.php @@ -0,0 +1,13 @@ +'; + foreach ($settingsErrors as $error) { + /** @var WP_Error $error */ + if (!($error instanceof WP_Error)) { + continue; + } + printf(' %s
', esc_attr($error->get_error_code()), esc_html($error->get_error_message())); + } + echo ''; +} + +if (!empty($_GET['no-changes-made'])) { + ?> +++ + +No changes were made.
+++ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/extras/modules/role-editor/role-editor.css b/extras/modules/role-editor/role-editor.css new file mode 100644 index 0000000..5484216 --- /dev/null +++ b/extras/modules/role-editor/role-editor.css @@ -0,0 +1,608 @@ +#rex-loading-message { + margin-top: 10px; } + +#rex-main-ui { + display: flex; + flex-direction: row; + margin-top: 10px; + width: 100%; } + +#rex-content-container, +#rex-action-sidebar { + border: 1px solid #ccd0d4; + background: #fff; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); } + +#rex-content-container { + display: flex; + flex-grow: 80; + padding: 0; + overflow-x: hidden; } + +#rex-action-sidebar { + box-sizing: border-box; + width: 170px; + flex-grow: 0; + flex-shrink: 0; + align-self: flex-start; + margin-left: 15px; + padding: 10px 8px; } + #rex-action-sidebar .rex-action-separator { + height: 10px; } + +#rex-category-sidebar { + width: 240px; + flex-grow: 0; + flex-shrink: 0; + position: relative; + border-right: 1px solid #ccd0d4; + padding: 10px 0; + background: #f8f8f8; } + #rex-category-sidebar > ul { + margin-top: 0; } + #rex-category-sidebar .rex-nav-item { + cursor: pointer; + margin: 0; + padding: 3px 8px 3px 10px; } + #rex-category-sidebar .rex-nav-item:hover { + background-color: #E5F3FF; } + #rex-category-sidebar .rex-selected-nav-item { + background-color: #CCE8FF; + box-shadow: 0px -1px 0px 0px #99D1FF, 0px 1px 0px 0px #99D1FF; } + #rex-category-sidebar .rex-selected-nav-item:hover { + background-color: #CCE8FF; } + #rex-category-sidebar .rex-nav-level-2 { + padding-left: 0px; } + #rex-category-sidebar .rex-nav-level-3 { + padding-left: 13px; } + #rex-category-sidebar .rex-nav-level-4 { + padding-left: 26px; } + #rex-category-sidebar .rex-nav-level-5 { + padding-left: 39px; } + #rex-category-sidebar .rex-nav-toggle { + visibility: hidden; + display: inline-block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + max-height: 100%; + width: 20px; + text-align: right; + vertical-align: middle; } + #rex-category-sidebar .rex-nav-toggle:after { + font-family: dashicons, sans-serif; + content: "\f345"; } + #rex-category-sidebar .rex-nav-toggle:hover { + color: #3ECEF9; } + #rex-category-sidebar .rex-nav-is-expanded .rex-nav-toggle:after { + content: "\f347"; } + #rex-category-sidebar .rex-nav-has-children .rex-nav-toggle { + visibility: visible; } + #rex-category-sidebar .rex-dropdown-trigger { + position: absolute; + right: 0; + top: 0; + padding: 12px 10px 3px 8px; } + #rex-category-sidebar .rex-nav-item { + display: flex; + flex-wrap: nowrap; + align-items: baseline; + height: 21px; + padding-top: 4px; + padding-bottom: 2px; } + #rex-category-sidebar .rex-nav-item .rex-nav-toggle { + flex-shrink: 0; + margin-right: 0.3em; + align-self: stretch; + padding: 1px 0; } + #rex-category-sidebar .rex-nav-item .rex-capability-count { + flex-shrink: 0; + margin-left: 0.3em; + margin-right: 0.3em; } + #rex-category-sidebar .rex-nav-item .rex-nav-item-header { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; } + +#rex-capability-view-container { + flex-grow: 70; + padding: 10px 10px; + overflow-x: hidden; } + +#rex-capability-view { + width: 100%; + display: flex; + flex-direction: row; + flex-wrap: wrap; } + +.rex-category { + box-sizing: border-box; + min-width: 160px; + width: 250px; + flex-grow: 0; + flex-shrink: 0; + flex-basis: auto; + padding: 0; + margin: 0 16px 16px 0; + border: 1px solid #ccd0d4; } + .rex-category .rex-category-name { + font-weight: 600; } + .rex-category .rex-category-subheading { + display: none; + color: #666; + font-size: 12px; + font-variant: small-caps; } + .rex-category .rex-category-subtitle { + color: #888; + font-size: 0.95em; + font-family: Consolas, Monaco, monospace; } + .rex-category .rex-category-contents { + box-sizing: border-box; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + padding: 10px; } + .rex-category.rex-has-subcategories { + width: 100%; + flex-basis: 100%; } + .rex-category .rex-category-header { + padding: 8px 10px; + border-bottom: 1px solid #ccd0d4; } + .rex-category.rex-top-category { + border: none; + margin: 0 0 10px 0; + padding: 0; } + .rex-category.rex-top-category > .rex-category-header { + color: #23282d; + font-size: 1.3em; + margin: 1em 0; + padding: 0; + border-bottom: none; } + .rex-category.rex-top-category > .rex-category-contents { + padding: 0; } + .rex-category.rex-sub-category { + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); } + .rex-category.rex-sub-category > .rex-category-header { + background: #fafafa; } + +.rex-desired-columns-1 { + width: 250px; + flex-grow: 0; + max-width: 500px; } + +.rex-desired-columns-2 { + width: 516px; + flex-grow: 0; + max-width: 1032px; } + +.rex-desired-columns-3 { + width: 782px; + flex-grow: 0; + max-width: 1564px; } + +.rex-desired-columns-max { + flex-basis: 100%; + width: 100%; } + +@media screen and (max-width: 1432px) { + .rex-desired-columns-3 { + flex-basis: 100%; + width: 100%; } } +@media screen and (max-width: 1168px) { + .rex-desired-columns-2 { + flex-basis: 100%; + width: 100%; } + + .rex-desired-columns-3 { + flex-basis: 100%; + width: 100%; } } +.rex-full-width-categories .rex-category { + width: 100%; + max-width: unset; } +.rex-full-width-categories .rex-desired-columns-1 > .rex-category-contents > .rex-permission-list { + column-count: 1; + max-width: 300px; } + +/* + * Ensure that each category contains no more than the desired number of columns. + * This is done by adding an invisible space filler element to the end of the permission list. + * + * Warning: This hack is not perfect. It can allow n+1 columns sometimes. + */ +@media screen and (min-width: 1292px) and (max-width: 1501px) { + .rex-full-width-categories .rex-desired-columns-2 > .rex-category-contents > .rex-permission-list::after { + content: 'filler'; + display: block; + background: yellowgreen; + font-size: 13px; + height: 81px; + visibility: hidden; } } +@media screen and (min-width: 1502px) and (max-width: 1711px) { + .rex-full-width-categories .rex-desired-columns-2 > .rex-category-contents > .rex-permission-list::after { + content: 'filler'; + display: block; + background: yellowgreen; + font-size: 13px; + height: 162px; + visibility: hidden; } + .rex-full-width-categories .rex-desired-columns-3 > .rex-category-contents > .rex-permission-list::after { + content: 'filler'; + display: block; + background: yellowgreen; + font-size: 13px; + height: 81px; + visibility: hidden; } } +@media screen and (min-width: 1712px) { + .rex-full-width-categories .rex-desired-columns-2 > .rex-category-contents > .rex-permission-list::after { + content: 'filler'; + display: block; + background: yellowgreen; + font-size: 13px; + height: 243px; + visibility: hidden; } + .rex-full-width-categories .rex-desired-columns-3 > .rex-category-contents > .rex-permission-list::after { + content: 'filler'; + display: block; + background: yellowgreen; + font-size: 13px; + height: 162px; + visibility: hidden; } + .rex-full-width-categories .rex-desired-columns-4 > .rex-category-contents > .rex-permission-list::after { + content: 'filler'; + display: block; + background: yellowgreen; + font-size: 13px; + height: 81px; + visibility: hidden; } } +.rex-show-category-subheadings .rex-category .rex-category-subheading { + display: block; + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } + +.rex-capability-count { + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + font-size: 12px; } + .rex-capability-count:before { + content: "("; } + .rex-capability-count:after { + content: ")"; } + +.rex-enabled-capability-count + .rex-total-capability-count:before { + content: "/"; } + +.rex-permission-list { + box-sizing: border-box; + width: 100%; + columns: 200px; + column-gap: 10px; } + +.rex-permission { + box-sizing: border-box; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 13px; + height: 27px; + vertical-align: baseline; + break-inside: avoid-column; + display: flex; } + .rex-permission label, .rex-permission .rex-permission-tip-trigger { + vertical-align: baseline; + padding-top: 3px; + padding-bottom: 3px; } + .rex-permission label { + flex-grow: 1; + flex-shrink: 1; + flex-basis: 50px; + overflow: hidden; + text-overflow: ellipsis; } + .rex-permission .rex-permission-tip-trigger { + flex-grow: 0; + flex-shrink: 0; + flex-basis: 20px; } + +.rex-is-redundant { + color: #888; } + +.rex-is-explicitly-denied input[type=checkbox] { + border-color: red; } + +.rex-is-personal-override.rex-is-explicitly-denied input[type=checkbox] { + background-color: #ffe5e5; } +.rex-is-personal-override input[type=checkbox]:checked { + background-color: #d9ffd9; + border-color: green; } + +.rex-permission-tip-trigger { + visibility: hidden; + display: inline-block; + min-width: 20px; + height: 100%; + margin: 0; + padding-left: 2px; + cursor: pointer; + color: #777; } + .rex-permission-tip-trigger:hover { + color: #0096dd; } + +.rex-permission:hover { + background-color: #fafafa; } + .rex-permission:hover .rex-permission-tip-trigger { + visibility: visible; } + +.rex-tooltip { + max-width: 700px; } + .rex-tooltip .rex-tooltip-section-container { + display: flex; + flex-direction: column; + flex-wrap: nowrap; } + .rex-tooltip .rex-tooltip-section { + max-width: 400px; } + +#rex-permission-tip { + overflow-y: auto; + max-height: 600px; } + #rex-permission-tip h4 { + margin-bottom: 0.4em; } + #rex-permission-tip .rex-tip-granted-permissions { + list-style: disc inside; + margin-top: 0; + margin-bottom: 0; } + #rex-permission-tip .rex-documentation-link { + display: inline-block; + max-width: 100%; + overflow-wrap: break-word; } + +.rex-capability-inheritance-breakdown tbody tr:nth-child(2n+1) { + background-color: #F9F9F9; } +.rex-capability-inheritance-breakdown .rex-is-decisive-actor td:first-child:after { + content: "\1f844"; + display: inline-block; + font-weight: bold; + margin-left: 0.5em; } + +#rex-view-toolbar { + background: #fcfcfc; + border-bottom: 1px solid #ddd; + padding: 0 8px 10px 8px; + margin: -10px -10px 0 -10px; + display: flex; + align-items: center; + flex-wrap: wrap; } + #rex-view-toolbar > * { + margin-top: 10px; } + #rex-view-toolbar .button { + vertical-align: middle; } + #rex-view-toolbar > label { + margin-right: 10px; } + #rex-view-toolbar .rex-dropdown-trigger .dashicons { + line-height: 1.3; } + +#rex-quick-search-query { + min-width: 250px; + max-width: 100%; + margin-right: 10px; } + +#rex-misc-view-options-button { + margin-left: auto; + margin-right: 10px; } + +.rex-search-highlight { + background-color: #ffff00; } + +.rex-permission-table th input[type="checkbox"] { + vertical-align: middle; + margin: -4px 4px -1px 0; } +.rex-permission-table tbody tr:nth-child(2n+1) { + background-color: #F9F9F9; } +.rex-permission-table td ul { + margin: 0; } +.rex-permission-table .rex-base-cap-notice { + color: #888; } + +/* Switch to fixed layout in narrow viewports to prevent overflow. */ +@media screen and (max-width: 1540px) { + .rex-permission-table { + table-layout: fixed; + max-width: 100%; } + .rex-permission-table .rex-category-name-column { + width: 20%; } + + .rex-readable-names-enabled .rex-permission-table { + table-layout: fixed; + max-width: 100%; } + .rex-readable-names-enabled .rex-permission-table .rex-category-name-column { + width: 25%; } } +/* The taxonomy table needs a wider screen because it has more columns. */ +@media screen and (max-width: 1650px) { + #rex-taxonomy-summary-category .rex-permission-table { + table-layout: fixed; + max-width: 100%; } + #rex-taxonomy-summary-category .rex-permission-table .rex-category-name-column { + width: 25%; } } +/* +When in "human readable" mode, the taxonomy table doesn't show capability names, +so it won't overflow its container unless the viewport is very small. +*/ +.rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table { + table-layout: auto; + max-width: 600px; } + .rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table .rex-capability-name, .rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table .rex-permission-tip-trigger { + display: none; } + .rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table .rex-permission, .rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table th[scope="col"] { + text-align: center; } + .rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table .rex-category-name-column { + width: unset; } + +@media screen and (max-width: 1200px) { + .rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table { + table-layout: fixed; + max-width: 100%; } + .rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table .rex-category-name-column { + width: 40%; } } +#rex-action-sidebar .rex-action-button { + display: block; + margin-bottom: 4px; + width: 100%; } + +#rex-permission-list-view { + column-width: 240px; + column-gap: 16px; + padding-top: 8px; } + +#rex-category-view-spacer { + width: 100%; + height: 10px; } + +.rex-dropdown-trigger { + display: inline-block; + box-sizing: border-box; + cursor: pointer; + padding: 2px; + color: #aaa; + text-decoration: none; } + .rex-dropdown-trigger:hover, .rex-dropdown-trigger:focus { + color: #777; + text-decoration: none; } + +.rex-dropdown { + position: absolute; + border: 1px solid #ccd0d4; + background: #fff; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2); + padding: 10px 8px; + z-index: 100; } + .rex-dropdown .rex-dropdown-item { + display: block; + margin-bottom: 10px; } + .rex-dropdown .rex-dropdown-item:last-child { + margin-bottom: 0; } + .rex-dropdown .rex-dropdown-sub-item { + margin-left: 1em; } + .rex-dropdown .rex-dropdown-item > .rex-dropdown-item { + margin-bottom: 6px; } + .rex-dropdown .rex-dropdown-item > .rex-dropdown-item:last-child { + margin-bottom: 0; } + +.ui-dialog .ui-dialog-buttonpane { + background: #fcfcfc; + border-top: 1px solid #dfdfdf; + padding: 8px; } + .ui-dialog .ui-dialog-buttonpane:after { + clear: both; + content: ""; + min-height: 0; + display: table; + border-collapse: collapse; } +.ui-dialog .ui-dialog-buttonset { + width: 100%; } + .ui-dialog .ui-dialog-buttonset .ui-button.rex-dialog-cancel-button, .ui-dialog .ui-dialog-buttonset .ui-button.ame-dialog-cancel-button { + float: right; + margin-right: 0 !important; } + .ui-dialog .ui-dialog-buttonset .ui-button { + float: left; } + +.rex-dialog input[type=text], .rex-dialog select { + box-sizing: border-box; + display: block; + width: 100%; } + +.rex-dialog-section { + margin-top: 0; } + +#rex-delete-capability-dialog .rex-deletable-capability-container { + max-height: 400px; + overflow-y: auto; } +#rex-delete-capability-dialog .rex-deletable-capability-list { + margin-top: 0; + list-style-type: none; } + +#rex-add-capability-dialog #rex-new-capability-name { + box-sizing: border-box; + width: 100%; } +#rex-add-capability-dialog #rex-add-capability-validation-message { + min-height: 40px; + margin-bottom: 6px; } + +#rex-delete-role-dialog .rex-deletable-role-list-container { + max-height: 380px; + overflow-y: auto; + margin-top: 10px; } +#rex-delete-role-dialog .rex-deletable-role-list { + table-layout: fixed; } + #rex-delete-role-dialog .rex-deletable-role-list tbody tr:nth-child(2n+1) { + background-color: #F9F9F9; } +#rex-delete-role-dialog .rex-role-name-column > label { + display: inline-block; + width: 100%; } +#rex-delete-role-dialog .rex-role-usage-column { + width: 6em; + max-width: 30%; + color: #888; + text-align: right; } + +#rex-editable-roles-container { + display: flex; } + #rex-editable-roles-container .ame-role-table { + min-width: 190px; + border: 1px solid #ccd0d4; + border-right-style: none; } + #rex-editable-roles-container .ame-role-table td { + cursor: pointer; } + #rex-editable-roles-container .ame-selected-role-table-row { + background: #CCE8FF; } + #rex-editable-roles-container .ame-selected-role-table-row .ame-selected-role-tip { + visibility: visible; } + #rex-editable-roles-container .ame-selected-role-table-row .ame-column-role-name { + font-weight: bold; } + #rex-editable-roles-container .ame-column-selected-role-tip { + position: relative; + padding: 0; + min-width: 30px; } + #rex-editable-roles-container .ame-selected-role-tip { + visibility: hidden; + height: 100%; + width: 100%; + box-sizing: border-box; + position: absolute; + top: 0; + right: -2px; + border-right: 1px solid white; } + #rex-editable-roles-container .ame-selected-role-tip .ame-rex-svg-triangle { + box-sizing: border-box; + position: absolute; + right: 0; + height: 100%; } + #rex-editable-roles-container .ame-selected-role-tip .ame-rex-svg-triangle polygon { + fill: white; + stroke: white; + stroke-width: 1px; } + +#rex-editable-roles-options { + padding: 4px 10px 10px 10px; + border: 1px solid #ccd0d4; } + #rex-editable-roles-options fieldset > p:first-of-type { + margin-top: 0; } + +#rex-editable-role-list { + margin-left: 1em; + margin-top: 0; } + +#rex-user-role-list { + border-right: 1px solid #ccd0d4; + padding: 10px 8px; + background: #f8f8f8; } + #rex-user-role-list p:first-child { + margin-top: 0; } + +#rex-primary-user-role { + display: block; } + +.rex-user-role-option-list { + margin-top: 0; } + +/*# sourceMappingURL=role-editor.css.map */ diff --git a/extras/modules/role-editor/role-editor.css.map b/extras/modules/role-editor/role-editor.css.map new file mode 100644 index 0000000..e58a17b --- /dev/null +++ b/extras/modules/role-editor/role-editor.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAwBA,oBAAqB;EACpB,UAAU,EAAE,IAAI;;AAGjB,YAAa;EACZ,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,GAAG;EACnB,UAAU,EAAE,IAAI;EAChB,KAAK,EAAE,IAAI;;AAGZ;mBACoB;EAvBnB,MAAM,EAXK,iBAAgC;EAY3C,UAAU,EAAE,IAAI;EAChB,UAAU,ECdQ,6BAA6B;;ADuChD,sBAAuB;EACtB,OAAO,EAAE,IAAI;EACb,SAAS,EAAE,EAAE;EACb,OAAO,EAAE,CAAC;EACV,UAAU,EAAE,MAAM;;AAGnB,mBAAoB;EACnB,UAAU,EAAE,UAAU;EACtB,KAAK,EAAE,KAAK;EACZ,SAAS,EAAE,CAAC;EACZ,WAAW,EAAE,CAAC;EACd,UAAU,EAAE,UAAU;EAEtB,WAAW,EAAE,IAAI;EACjB,OAAO,EAlDK,QAA8B;EAoD1C,yCAAsB;IACrB,MAAM,EAAE,IAAI;;AAId,qBAAsB;EACrB,KAAK,EAAE,KAAK;EACZ,SAAS,EAAE,CAAC;EACZ,WAAW,EAAE,CAAC;EACd,QAAQ,EAAE,QAAQ;EAElB,YAAY,EAlED,iBAAgC;EAmE3C,OAAO,EAAE,MAAgB;EAEzB,UAAU,EAAE,OAAO;EAEnB,0BAAO;IACN,UAAU,EAAE,CAAC;EAGd,mCAAc;IACb,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,gBAAoD;EAG9D,yCAAoB;IACnB,gBAAgB,EAAE,OAAO;EAG1B,4CAAuB;IACtB,gBAAgB,EAAE,OAAO;IACzB,UAAU,EAAE,iDAAiD;IAG7D,kDAAQ;MACP,gBAAgB,EAAE,OAAO;EAM1B,sCAAyB;IACxB,YAAY,EAAE,GAAmC;EADlD,sCAAyB;IACxB,YAAY,EAAE,IAAmC;EADlD,sCAAyB;IACxB,YAAY,EAAE,IAAmC;EADlD,sCAAyB;IACxB,YAAY,EAAE,IAAmC;EAInD,qCAAgB;IACf,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,YAAY;IAErB,kBAAkB,EAAE,UAAU;IAC9B,eAAe,EAAE,UAAU;IAC3B,UAAU,EAAE,UAAU;IAEtB,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,IAAI;IAEX,UAAU,EAAE,KAAK;IACjB,cAAc,EAAE,MAAM;IAEtB,2CAAQ;MACP,WAAW,EAAE,qBAAqB;MAClC,OAAO,EAAE,OAAO;IAGjB,2CAAQ;MACP,KAAK,EAAE,OAAO;EAMf,gEAAQ;IACP,OAAO,EAAE,OAAO;EAIlB,2DAAsC;IACrC,UAAU,EAAE,OAAO;EAGpB,2CAAsB;IACrB,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,CAAC;IACR,GAAG,EAAE,CAAC;IAEN,OAAO,EAAE,iBAA8D;EAIxE,mCAAc;IACb,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,MAAM;IACjB,WAAW,EAAE,QAAQ;IAErB,MAAM,EAAE,IAAI;IACZ,WAAW,EAAE,GAAG;IAChB,cAAc,EAAE,GAAG;IAInB,mDAAgB;MACf,WAAW,EAAE,CAAC;MACd,YAAY,EAJA,KAAK;MAKjB,UAAU,EAAE,OAAO;MACnB,OAAO,EAAE,KAAK;IAGf,yDAAsB;MACrB,WAAW,EAAE,CAAC;MACd,WAAW,EAXC,KAAK;MAYjB,YAAY,EAZA,KAAK;IAelB,wDAAqB;MACpB,WAAW,EAAE,MAAM;MACnB,aAAa,EAAE,QAAQ;MACvB,QAAQ,EAAE,MAAM;;AAKnB,8BAA+B;EAC9B,SAAS,EAAE,EAAE;EACb,OAAO,EAAE,SAAwD;EACjE,UAAU,EAAE,MAAM;;AAGnB,oBAAqB;EACpB,KAAK,EAAE,IAAI;EAEX,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,GAAG;EACnB,SAAS,EAAE,IAAI;;AAMhB,aAAc;EACb,UAAU,EAAE,UAAU;EACtB,SAAS,EAAE,KAAK;EAChB,KAAK,EANa,KAAK;EAQvB,SAAS,EAAE,CAAC;EACZ,WAAW,EAAE,CAAC;EACd,UAAU,EAAE,IAAI;EAEhB,OAAO,EAAE,CAAC;EACV,MAAM,EAAE,aAAuC;EAE/C,MAAM,EAAE,iBAAgC;EAExC,gCAAmB;IAClB,WAAW,EAAE,GAAG;EAGjB,sCAAyB;IACxB,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,IAAI;IACX,SAAS,EAAE,IAAI;IACf,YAAY,EAAE,UAAU;EAGzB,oCAAuB;IACtB,KAAK,EAAE,IAAI;IACX,SAAS,EAAE,MAAM;IACjB,WAAW,EAAE,2BAA2B;EAGzC,oCAAuB;IACtB,UAAU,EAAE,UAAU;IAEtB,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,IAAI;IACf,eAAe,EAAE,UAAU;IAE3B,OAAO,EAAE,IAAI;EAGd,mCAAwB;IACvB,KAAK,EAAE,IAAI;IACX,UAAU,EAAE,IAAI;EAGjB,kCAAqB;IACpB,OAAO,EAAE,QAAQ;IACjB,aAAa,EAAE,iBAAgC;EAGhD,8BAAmB;IAClB,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,CAAC;IAEV,qDAAyB;MACxB,KAAK,EAAE,OAAO;MACd,SAAS,EAAE,KAAK;MAChB,MAAM,EAAE,KAAK;MACb,OAAO,EAAE,CAAC;MACV,aAAa,EAAE,IAAI;IAGpB,uDAA2B;MAC1B,OAAO,EAAE,CAAC;EAIZ,8BAAmB;IAClB,UAAU,EC1QO,6BAA6B;ID4Q9C,qDAAyB;MACxB,UAAU,EAAE,OAAO;;AAWrB,sBAA8B;EAE7B,KAAK,EADU,KAA2B;EAE1C,SAAS,EAAE,CAAC;EACZ,SAAS,EAAE,KAAiB;;AAJ7B,sBAA8B;EAE7B,KAAK,EADU,KAA2B;EAE1C,SAAS,EAAE,CAAC;EACZ,SAAS,EAAE,MAAiB;;AAJ7B,sBAA8B;EAE7B,KAAK,EADU,KAA2B;EAE1C,SAAS,EAAE,CAAC;EACZ,SAAS,EAAE,MAAiB;;AAI9B,wBAAyB;EACxB,UAAU,EAAE,IAAI;EAChB,KAAK,EAAE,IAAI;;AAKZ,qCAAsC;EAKnC,sBAA8B;IAC7B,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,IAAI;AAMf,qCAAsC;EAKnC,sBAA8B;IAC7B,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,IAAI;;EAFZ,sBAA8B;IAC7B,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,IAAI;AASd,wCAAc;EACb,KAAK,EAAE,IAAI;EACX,SAAS,EAAE,KAAK;AAGjB,iGAAuE;EACtE,YAAY,EAAE,CAAC;EACf,SAAS,EAAE,KAAK;;AAIlB;;;;;GAKG;AAqBH,6DAA8D;EAd1D,wGAAqF;IACpF,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,IAAI;IAEf,MAAM,EAAE,IAA4E;IACpF,UAAU,EAAE,MAAM;AAWvB,6DAA8D;EAlB1D,wGAAqF;IACpF,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,IAAI;IAEf,MAAM,EAAE,KAA4E;IACpF,UAAU,EAAE,MAAM;EAPnB,wGAAqF;IACpF,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,IAAI;IAEf,MAAM,EAAE,IAA4E;IACpF,UAAU,EAAE,MAAM;AAevB,qCAAsC;EAtBlC,wGAAqF;IACpF,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,IAAI;IAEf,MAAM,EAAE,KAA4E;IACpF,UAAU,EAAE,MAAM;EAPnB,wGAAqF;IACpF,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,IAAI;IAEf,MAAM,EAAE,KAA4E;IACpF,UAAU,EAAE,MAAM;EAPnB,wGAAqF;IACpF,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,IAAI;IAEf,MAAM,EAAE,IAA4E;IACpF,UAAU,EAAE,MAAM;AAqBvB,qEAAsE;EACrE,OAAO,EAAE,KAAK;EACd,KAAK,EAAE,IAAI;EAEX,WAAW,EAAE,MAAM;EACnB,QAAQ,EAAE,MAAM;EAChB,aAAa,EAAE,QAAQ;;AAGxB,qBAAsB;EAOrB,qBAAqB,EAAE,IAAI;EAC3B,kBAAkB,EAAE,IAAI;EACxB,aAAa,EAAE,IAAI;EAEnB,SAAS,EAAE,IAAI;EAUf,4BAAS;IACR,OAAO,EAAE,GAAG;EAGb,2BAAQ;IACP,OAAO,EAAE,GAAG;;AAKb,kEAAS;EACR,OAAO,EAAE,GAAG;;AAId,oBAAqB;EACpB,UAAU,EAAE,UAAU;EACtB,KAAK,EAAE,IAAI;EAEX,OAAO,EAAE,KAAK;EACd,UAAU,EAAE,IAAI;;AAQjB,eAAgB;EACf,UAAU,EAAE,UAAU;EAEtB,WAAW,EAAE,MAAM;EACnB,QAAQ,EAAE,MAAM;EAChB,aAAa,EAAE,QAAQ;EAEvB,SAAS,EAAE,IAAI;EACf,MAAM,EAhbe,IAAI;EAibzB,cAAc,EAAE,QAAQ;EAExB,YAAY,EAAE,YAAY;EAE1B,OAAO,EAAE,IAAI;EAEb,kEAAmC;IAClC,cAAc,EAAE,QAAQ;IACxB,WAAW,EAAE,GAAG;IAChB,cAAc,EAAE,GAAG;EAGpB,qBAAM;IACL,SAAS,EAAE,CAAC;IACZ,WAAW,EAAE,CAAC;IACd,UAAU,EAAE,IAAI;IAEhB,QAAQ,EAAE,MAAM;IAChB,aAAa,EAAE,QAAQ;EAGxB,2CAA4B;IAC3B,SAAS,EAAE,CAAC;IACZ,WAAW,EAAE,CAAC;IACd,UAAU,EAAE,IAAI;;AAIlB,iBAAkB;EACjB,KAAK,EAAE,IAAI;;AAGZ,8CAA+C;EAC9C,YAAY,EAAE,GAAG;;AAQjB,uEAAgD;EAJhD,gBAAgB,EAKY,OAAO;AAGnC,sDAA6B;EAR7B,gBAAgB,EASY,OAAO;EAClC,YAAY,EAAE,KAAK;;AASrB,2BAA4B;EAE3B,UAAU,EAAE,MAAM;EAElB,OAAO,EAAE,YAAY;EACrB,SAAS,EAAE,IAAI;EACf,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,CAAC;EACT,YAAY,EAAE,GAAG;EAEjB,MAAM,EAAE,OAAO;EACf,KAAK,EAAE,IAAI;EAEX,iCAAQ;IACP,KAAK,EAAE,OAAO;;AAIhB,qBAAsB;EACrB,gBAAgB,EAAE,OAAO;EAEzB,iDAA4B;IAC3B,UAAU,EAAE,OAAO;;AAIrB,YAAa;EACZ,SAAS,EAAE,KAAK;EAEhB,2CAA+B;IAC9B,OAAO,EAAE,IAAI;IACb,cAAc,EAAE,MAAM;IACtB,SAAS,EAAE,MAAM;EAGlB,iCAAqB;IACpB,SAAS,EAAE,KAAK;;AAIlB,mBAAoB;EACnB,UAAU,EAAE,IAAI;EAChB,UAAU,EAAE,KAAK;EAEjB,sBAAG;IACF,aAAa,EAAE,KAAK;EAGrB,gDAA6B;IAC5B,UAAU,EAAE,WAAW;IACvB,UAAU,EAAE,CAAC;IACb,aAAa,EAAE,CAAC;EAGjB,2CAAwB;IACvB,OAAO,EAAE,YAAY;IACrB,SAAS,EAAE,IAAI;IACf,aAAa,EAAE,UAAU;;AAzhB1B,8DAAyB;EACxB,gBAAgB,EAAE,OAAO;AAgiBzB,iFAAqB;EACpB,OAAO,EAAE,QAAQ;EACjB,OAAO,EAAE,YAAY;EACrB,WAAW,EAAE,IAAI;EACjB,WAAW,EAAE,KAAK;;AAOrB,iBAAkB;EACjB,UAAU,EAAE,OAAO;EACnB,aAAa,EAAE,cAAc;EAE7B,OAAO,EAAE,cAAgD;EACzD,MAAM,EAAE,mBAAkG;EAE1G,OAAO,EAAE,IAAI;EACb,WAAW,EAAE,MAAM;EACnB,SAAS,EAAE,IAAI;EAEf,qBAAM;IACL,UAAU,EAxkBI,IAAI;EA2kBnB,yBAAQ;IACP,cAAc,EAAE,MAAM;EAGvB,yBAAQ;IACP,YAAY,EAAE,IAAI;EAGnB,kDAAiC;IAChC,WAAW,EAAE,GAAG;;AAIlB,uBAAwB;EACvB,SAAS,EAAE,KAAK;EAChB,SAAS,EAAE,IAAI;EACf,YAAY,EAAE,IAAI;;AAGnB,6BAA8B;EAC7B,WAAW,EAAE,IAAI;EACjB,YAAY,EAAE,IAAI;;AAQnB,qBAAsB;EACrB,gBAAgB,EAAE,OAAO;;AAKzB,+CAA0B;EACzB,cAAc,EAAE,MAAM;EACtB,MAAM,EAAE,eAAe;AAhmBxB,8CAAyB;EACxB,gBAAgB,EAAE,OAAO;AAomB1B,2BAAM;EACL,MAAM,EAAE,CAAC;AAGV,0CAAqB;EACpB,KAAK,EAAE,IAAI;;AAIb,qEAAqE;AAUrE,qCAAsC;EACrC,qBAAsB;IATtB,YAAY,EAAE,KAAK;IACnB,SAAS,EAAE,IAAI;IAEf,+CAA0B;MACzB,KAAK,EAMqB,GAAG;;EAG9B,iDAAkD;IAblD,YAAY,EAAE,KAAK;IACnB,SAAS,EAAE,IAAI;IAEf,2EAA0B;MACzB,KAAK,EAUqB,GAAG;AAI/B,0EAA0E;AAC1E,qCAAsC;EACrC,oDAAqD;IApBrD,YAAY,EAAE,KAAK;IACnB,SAAS,EAAE,IAAI;IAEf,8EAA0B;MACzB,KAAK,EAiBqB,GAAG;AAI/B;;;EAGE;AACF,gFAAiF;EAChF,YAAY,EAAE,IAAI;EAClB,SAAS,EAAE,KAAK;EAEhB,mNAAkD;IACjD,OAAO,EAAE,IAAI;EAGd,kMAAiC;IAChC,UAAU,EAAE,MAAM;EAGnB,0GAA0B;IACzB,KAAK,EAAE,KAAK;;AAId,qCAAsC;EACrC,gFAAiF;IA/CjF,YAAY,EAAE,KAAK;IACnB,SAAS,EAAE,IAAI;IAEf,0GAA0B;MACzB,KAAK,EA4CqB,GAAG;AAO9B,sCAAmB;EAClB,OAAO,EAAE,KAAK;EACd,aAAa,EAAE,GAAG;EAClB,KAAK,EAAE,IAAI;;AAIb,yBAA0B;EACzB,YAAY,EAAE,KAAK;EACnB,UAAU,EAAE,IAAI;EAChB,WAAW,EAhsBK,GAAG;;AAmsBpB,yBAA0B;EACzB,KAAK,EAAE,IAAI;EACX,MAAM,EAjsBsB,IAAc;;AAosB3C,qBAAsB;EACrB,OAAO,EAAE,YAAY;EACrB,UAAU,EAAE,UAAU;EACtB,MAAM,EAAE,OAAO;EAEf,OAAO,EAAE,GAAG;EACZ,KAAK,EAAE,IAAI;EACX,eAAe,EAAE,IAAI;EAErB,wDAAiB;IAChB,KAAK,EAAE,IAAI;IACX,eAAe,EAAE,IAAI;;AAIvB,aAAc;EACb,QAAQ,EAAE,QAAQ;EAElB,MAAM,EA5tBK,iBAAgC;EA6tB3C,UAAU,EAAE,IAAI;EAChB,UAAU,EAAE,4BAA4B;EAExC,OAAO,EA7tBK,QAA8B;EA8tB1C,OAAO,EAAE,GAAG;EAEZ,gCAAmB;IAClB,OAAO,EAAE,KAAK;IACd,aAAa,EAAE,IAAI;IAEnB,2CAAa;MACZ,aAAa,EAAE,CAAC;EAIlB,oCAAuB;IACtB,WAAW,EAAE,GAAG;EAGjB,qDAAwC;IACvC,aAAa,EAAE,GAAG;IAElB,gEAAa;MACZ,aAAa,EAAE,CAAC;;AAMlB,gCAAsB;EACrB,UAAU,EAAE,OAAO;EACnB,UAAU,EAAE,iBAAiB;EAC7B,OAAO,EAAE,GAAG;EAEZ,sCAAQ;IACP,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,EAAE;IACX,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,KAAK;IACd,eAAe,EAAE,QAAQ;AAM3B,+BAAqB;EACpB,KAAK,EAAE,IAAI;EAEX,wIAAyE;IACxE,KAAK,EAAE,KAAK;IACZ,YAAY,EAAE,YAAY;EAG3B,0CAAW;IACV,KAAK,EAAE,IAAI;;AAMb,gDAAyB;EACxB,UAAU,EAAE,UAAU;EACtB,OAAO,EAAE,KAAK;EACd,KAAK,EAAE,IAAI;;AAIb,mBAAoB;EACnB,UAAU,EAAE,CAAC;;AAIb,iEAAoC;EACnC,UAAU,EAAE,KAAK;EACjB,UAAU,EAAE,IAAI;AAGjB,4DAA+B;EAC9B,UAAU,EAAE,CAAC;EACb,eAAe,EAAE,IAAI;;AAKtB,mDAAyB;EACxB,UAAU,EAAE,UAAU;EACtB,KAAK,EAAE,IAAI;AAGZ,iEAAuC;EACtC,UAAU,EAAE,IAAI;EAChB,aAAa,EAAE,GAAG;;AAKnB,0DAAmC;EAClC,UAAU,EAAE,KAAK;EAEjB,UAAU,EAAE,IAAI;EAChB,UAAU,EAAE,IAAI;AAGjB,gDAAyB;EACxB,YAAY,EAAE,KAAK;EApzBpB,yEAAyB;IACxB,gBAAgB,EAAE,OAAO;AAuzB1B,qDAA8B;EAC7B,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,IAAI;AAGZ,8CAAuB;EACtB,KAAK,EAAE,GAAG;EACV,SAAS,EAAE,GAAG;EACd,KAAK,EAAE,IAAI;EACX,UAAU,EAAE,KAAK;;AAMnB,6BAA8B;EAC7B,OAAO,EAAE,IAAI;EAEb,6CAAgB;IACf,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,iBAAqC;IAC7C,kBAAkB,EAAE,IAAI;IAExB,gDAAG;MACF,MAAM,EAAE,OAAO;EAMjB,0DAA6B;IAC5B,UAAU,EAAE,OAAO;IAEnB,iFAAuB;MACtB,UAAU,EAAE,OAAO;IAGpB,gFAAsB;MACrB,WAAW,EAAE,IAAI;EAInB,2DAA8B;IAC7B,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,CAAC;IACV,SAAS,EAAE,IAAI;EAGhB,oDAAuB;IACtB,UAAU,EAAE,MAAM;IAElB,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;IACX,UAAU,EAAE,UAAU;IAEtB,QAAQ,EAAE,QAAQ;IAClB,GAAG,EAAE,CAAC;IACN,KAAK,EAAE,IAAI;IAEX,YAAY,EAAE,eAA8B;IAE5C,0EAAsB;MACrB,UAAU,EAAE,UAAU;MACtB,QAAQ,EAAE,QAAQ;MAClB,KAAK,EAAE,CAAC;MACR,MAAM,EAAE,IAAI;MAEZ,kFAAQ;QACP,IAAI,EAxCe,KAAK;QAyCxB,MAAM,EAzCa,KAAK;QA0CxB,YAAY,EAAE,GAAG;;AAMrB,2BAA4B;EAC3B,OAAO,EAAE,kBAAgD;EACzD,MAAM,EAAE,iBAAqC;EAE7C,sDAA2B;IAC1B,UAAU,EAAE,CAAC;;AAIf,uBAAwB;EACvB,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,CAAC;;AAMd,mBAAoB;EACnB,YAAY,EAv6BD,iBAAgC;EAw6B3C,OAAO,EAr6BK,QAA8B;EAu6B1C,UAAU,EAAE,OAAO;EAEnB,iCAAc;IACb,UAAU,EAAE,CAAC;;AAIf,sBAAuB;EACtB,OAAO,EAAE,KAAK;;AAGf,0BAA2B;EAC1B,UAAU,EAAE,CAAC", +"sources": ["role-editor.scss","../../../css/_boxes.scss"], +"names": [], +"file": "role-editor.css" +} \ No newline at end of file diff --git a/extras/modules/role-editor/role-editor.js b/extras/modules/role-editor/role-editor.js new file mode 100644 index 0000000..521bd0d --- /dev/null +++ b/extras/modules/role-editor/role-editor.js @@ -0,0 +1,3072 @@ +///Loading...+ + + + + + + + + + + + + + + + + + + + + ++/// +/// +/// +/// +/// +/// +/// +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var RexPermission = /** @class */ (function () { + function RexPermission(editor, capability) { + this.readableAction = null; + this.mainDescription = ''; + this.isRedundant = false; + this.editor = editor; + this.capability = capability; + var self = this; + this.labelHtml = ko.pureComputed({ + read: self.getLabelHtml, + deferEvaluation: true, + owner: this + }); + //Prevent freezing when entering a search query. Highlighting keywords in hundreds of capabilities can be slow. + this.labelHtml.extend({ rateLimit: { timeout: 50, method: "notifyWhenChangesStop" } }); + this.isVisible = ko.computed({ + read: function () { + if (!editor.capabilityMatchesFilters(self.capability)) { + return false; + } + //When in list view, check if the capability is inside the selected category. + if (editor.categoryViewMode() === RexRoleEditor.listView) { + if (!editor.isInSelectedCategory(self.capability.name)) { + return false; + } + } + if (self.capability.isDeleted()) { + return false; + } + return !(self.isRedundant && !editor.showRedundantEnabled()); + }, + owner: this, + deferEvaluation: true + }); + } + RexPermission.prototype.getLabelHtml = function () { + var text; + if ((this.readableAction !== null) && this.editor.readableNamesEnabled()) { + text = this.readableAction; + } + else { + text = this.capability.displayName(); + } + var html = wsAmeLodash.escape(text); + if (this.isVisible()) { + html = this.editor.highlightSearchKeywords(html); + } + //Let the browser break words on underscores. + html = html.replace(/_/g, '_ '); + return html; + }; + return RexPermission; +}()); +/** + * A basic representation of any component or extension that can add new capabilities. + * This includes plugins, themes, and the WordPress core. + */ +var RexWordPressComponent = /** @class */ (function () { + function RexWordPressComponent(id, name) { + this.id = id; + this.name = name; + } + RexWordPressComponent.fromJs = function (id, details) { + var instance = new RexWordPressComponent(id, details.name ? details.name : id); + if (details.capabilityDocumentationUrl) { + instance.capabilityDocumentationUrl = details.capabilityDocumentationUrl; + } + return instance; + }; + return RexWordPressComponent; +}()); +var RexObservableCapabilityMap = /** @class */ (function () { + function RexObservableCapabilityMap(initialCapabilities) { + this.capabilities = {}; + if (initialCapabilities) { + this.initialCapabilities = wsAmeLodash.clone(initialCapabilities); + } + else { + this.initialCapabilities = {}; + } + } + RexObservableCapabilityMap.prototype.getCapabilityState = function (capabilityName) { + var observable = this.getObservable(capabilityName); + return observable(); + }; + RexObservableCapabilityMap.prototype.setCapabilityState = function (capabilityName, state) { + var observable = this.getObservable(capabilityName); + observable(state); + }; + RexObservableCapabilityMap.prototype.getAllCapabilities = function () { + var _ = wsAmeLodash; + var result = this.initialCapabilities ? _.clone(this.initialCapabilities) : {}; + _.forEach(this.capabilities, function (observable, name) { + var isGranted = observable(); + if (isGranted === null) { + delete result[name]; + } + else { + result[name] = isGranted; + } + }); + return result; + }; + RexObservableCapabilityMap.prototype.getObservable = function (capabilityName) { + if (!this.capabilities.hasOwnProperty(capabilityName)) { + var initialValue = null; + if (this.initialCapabilities.hasOwnProperty(capabilityName)) { + initialValue = this.initialCapabilities[capabilityName]; + } + this.capabilities[capabilityName] = ko.observable(initialValue); + } + return this.capabilities[capabilityName]; + }; + return RexObservableCapabilityMap; +}()); +var RexBaseActor = /** @class */ (function () { + function RexBaseActor(id, name, displayName, capabilities) { + this.canHaveRoles = false; + this.id = ko.observable(id); + this.name = ko.observable(name); + this.displayName = ko.observable(displayName); + this.capabilities = new RexObservableCapabilityMap(capabilities || {}); + } + RexBaseActor.prototype.hasCap = function (capability) { + return (this.capabilities.getCapabilityState(capability) === true); + }; + RexBaseActor.prototype.getCapabilityState = function (capability) { + return this.getOwnCapabilityState(capability); + }; + RexBaseActor.prototype.getOwnCapabilityState = function (capability) { + return this.capabilities.getCapabilityState(capability); + }; + RexBaseActor.prototype.setCap = function (capability, enabled) { + this.capabilities.setCapabilityState(capability, enabled); + }; + RexBaseActor.prototype.deleteCap = function (capability) { + this.capabilities.setCapabilityState(capability, null); + }; + RexBaseActor.prototype.getDisplayName = function () { + return this.displayName(); + }; + RexBaseActor.prototype.getId = function () { + return this.id(); + }; + /** + * Get capabilities that are explicitly assigned/denied to this actor. + * Does not include capabilities that a user inherits from their role(s). + */ + RexBaseActor.prototype.getOwnCapabilities = function () { + return this.capabilities.getAllCapabilities(); + }; + return RexBaseActor; +}()); +var RexRole = /** @class */ (function (_super) { + __extends(RexRole, _super); + function RexRole(name, displayName, capabilities) { + var _this = _super.call(this, 'role:' + name, name, displayName, capabilities) || this; + _this.hasUsers = false; + return _this; + } + RexRole.fromRoleData = function (data) { + var role = new RexRole(data.name, data.displayName, data.capabilities); + role.hasUsers = data.hasUsers; + return role; + }; + /** + * Is this one of the default roles that are part of WordPress core? + * + * Note: I'm calling this property "built-in" instead of "default" to distinguish it + * from the default role for new users. + */ + RexRole.prototype.isBuiltIn = function () { + return RexRole.builtInRoleNames.indexOf(this.name()) >= 0; + }; + RexRole.prototype.toJs = function () { + return { + name: this.name(), + displayName: this.displayName(), + capabilities: this.getOwnCapabilities() + }; + }; + RexRole.builtInRoleNames = ['administrator', 'editor', 'author', 'subscriber', 'contributor']; + return RexRole; +}(RexBaseActor)); +var RexSuperAdmin = /** @class */ (function (_super) { + __extends(RexSuperAdmin, _super); + function RexSuperAdmin() { + return _super.call(this, 'special:super_admin', 'Super Admin', 'Super Admin') || this; + } + RexSuperAdmin.getInstance = function () { + if (RexSuperAdmin.instance === null) { + RexSuperAdmin.instance = new RexSuperAdmin(); + } + return RexSuperAdmin.instance; + }; + RexSuperAdmin.instance = null; + return RexSuperAdmin; +}(RexBaseActor)); +var RexUser = /** @class */ (function (_super) { + __extends(RexUser, _super); + function RexUser(login, displayName, capabilities, userId) { + var _this = _super.call(this, 'user:' + login, login, displayName, capabilities) || this; + _this.isSuperAdmin = false; + _this.userLogin = login; + _this.canHaveRoles = true; + _this.roles = ko.observableArray([]); + _this.userId = userId; + return _this; + } + RexUser.prototype.hasCap = function (capability, outGrantedBy) { + return (this.getCapabilityState(capability, outGrantedBy) === true); + }; + RexUser.prototype.getCapabilityState = function (capability, outGrantedBy) { + if (capability === 'do_not_allow') { + return false; + } + if (this.isSuperAdmin) { + if (outGrantedBy) { + outGrantedBy.push(RexSuperAdmin.getInstance()); + } + return (capability !== 'do_not_allow'); + } + var result = _super.prototype.getCapabilityState.call(this, capability); + if (result !== null) { + if (outGrantedBy) { + outGrantedBy.push(this); + } + return result; + } + wsAmeLodash.each(this.roles(), function (role) { + var roleHasCap = role.getCapabilityState(capability); + if (roleHasCap !== null) { + if (outGrantedBy) { + outGrantedBy.push(role); + } + result = roleHasCap; + } + }); + return result; + }; + // noinspection JSUnusedGlobalSymbols Used in KO templates. + RexUser.prototype.getInheritanceDetails = function (capability) { + var _ = wsAmeLodash; + var results = []; + //Note: Alternative terms include "Assigned", "Granted", "Yes"/"No". + if (this.isSuperAdmin) { + var superAdmin = RexSuperAdmin.getInstance(); + var description_1 = 'Allow everything'; + if (capability.name === 'do_not_allow') { + description_1 = 'Deny'; + } + results.push({ + actor: superAdmin, + name: superAdmin.displayName(), + description: description_1 + }); + } + _.each(this.roles(), function (role) { + var roleHasCap = role.getCapabilityState(capability.name); + var description; + if (roleHasCap) { + description = 'Allow'; + } + else if (roleHasCap === null) { + description = '—'; + } + else { + description = 'Deny'; + } + results.push({ + actor: role, + name: role.displayName(), + description: description, + }); + }); + var hasOwnCap = _super.prototype.getCapabilityState.call(this, capability.name); + var description; + if (hasOwnCap) { + description = 'Allow'; + } + else if (hasOwnCap === null) { + description = '—'; + } + else { + description = 'Deny'; + } + results.push({ + actor: this, + name: 'User-specific setting', + description: description, + }); + var relevantActors = []; + this.getCapabilityState(capability.name, relevantActors); + var decidingActor = _.last(relevantActors); + _.each(results, function (item) { + item.isDecisive = (item.actor === decidingActor); + }); + return results; + }; + RexUser.fromAmeUser = function (data, editor) { + var user = new RexUser(data.userLogin, data.displayName, data.capabilities, data.userId); + wsAmeLodash.forEach(data.roles, function (roleId) { + var role = editor.getRole(roleId); + if (role) { + user.roles.push(role); + } + }); + return user; + }; + RexUser.fromAmeUserProperties = function (properties, editor) { + var user = new RexUser(properties.user_login, properties.display_name, properties.capabilities); + if (properties.id) { + user.userId = properties.id; + } + wsAmeLodash.forEach(properties.roles, function (roleId) { + var role = editor.getRole(roleId); + if (role) { + user.roles.push(role); + } + }); + return user; + }; + RexUser.prototype.toJs = function () { + var _ = wsAmeLodash; + var roles = _.invoke(this.roles(), 'name'); + return { + userId: this.userId, + userLogin: this.userLogin, + displayName: this.displayName(), + capabilities: this.getOwnCapabilities(), + roles: roles + }; + }; + return RexUser; +}(RexBaseActor)); +var RexCategory = /** @class */ (function () { + function RexCategory(name, editor, slug, capabilities) { + if (slug === void 0) { slug = null; } + if (capabilities === void 0) { capabilities = []; } + var _this = this; + this.slug = null; + this.origin = null; + this.subtitle = null; + this.htmlId = null; + this.parent = null; + this.subcategories = []; + this.duplicates = []; + var _ = wsAmeLodash; + var self = this; + this.editor = editor; + this.name = name; + this.slug = slug; + if ((this.slug !== null) && (this.slug !== '')) { + editor.categoriesBySlug[this.slug] = this; + } + var initialPermissions = _.map(capabilities, function (capabilityName) { + return new RexPermission(editor, editor.getCapability(capabilityName)); + }); + this.permissions = ko.observableArray(initialPermissions); + this.sortPermissions(); + this.contentTemplate = ko.observable('rex-default-category-content-template'); + this.isSelected = ko.observable(false); + this.enabledCapabilityCount = ko.pureComputed({ + read: function () { + return self.countUniqueCapabilities({}, function (capability) { + return capability.isEnabledForSelectedActor(); + }); + }, + deferEvaluation: true, + owner: this + }); + this.enabledCapabilityCount.extend({ rateLimit: { timeout: 5, method: "notifyWhenChangesStop" } }); + this.totalCapabilityCount = ko.pureComputed({ + read: function () { + return self.countUniqueCapabilities(); + }, + deferEvaluation: true, + owner: this + }); + this.isCapCountVisible = ko.pureComputed({ + read: function () { + if (!editor.showNumberOfCapsEnabled()) { + return false; + } + var totalCaps = self.totalCapabilityCount(), enabledCaps = self.enabledCapabilityCount(); + if (!editor.showZerosEnabled() && ((totalCaps === 0) || (enabledCaps === 0))) { + return false; + } + return editor.showTotalCapCountEnabled() || self.isEnabledCapCountVisible(); + }, + deferEvaluation: true, + owner: this + }); + this.isEnabledCapCountVisible = ko.pureComputed({ + read: function () { + if (!editor.showGrantedCapCountEnabled()) { + return false; + } + return (self.enabledCapabilityCount() > 0) || editor.showZerosEnabled(); + }, + deferEvaluation: true, + owner: this + }); + this.areAllPermissionsEnabled = ko.computed({ + read: function () { + var items = self.permissions(); + var len = items.length; + for (var i = 0; i < len; i++) { + if (!items[i].capability.isEnabledForSelectedActor() && items[i].capability.isEditable()) { + return false; + } + } + for (var i = 0; i < self.subcategories.length; i++) { + if (!self.subcategories[i].areAllPermissionsEnabled()) { + return false; + } + } + return true; + }, + write: function (enabled) { + var items = self.permissions(); + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.capability.isEditable()) { + item.capability.isEnabledForSelectedActor(enabled); + } + } + for (var i = 0; i < self.subcategories.length; i++) { + self.subcategories[i].areAllPermissionsEnabled(enabled); + } + }, + deferEvaluation: true, + owner: this + }); + this.areAllPermissionsEnabled.extend({ rateLimit: { timeout: 5, method: 'notifyWhenChangesStop' } }); + this.areAnyPermissionsEditable = ko.pureComputed({ + read: function () { + var items = self.permissions(); + var len = items.length; + for (var i = 0; i < len; i++) { + if (items[i].capability.isEditable()) { + return true; + } + } + for (var i = 0; i < self.subcategories.length; i++) { + if (!self.subcategories[i].areAnyPermissionsEditable()) { + return true; + } + } + return false; + }, + deferEvaluation: true, + owner: this + }); + this.areAnyPermissionsEditable.extend({ rateLimit: { timeout: 5, method: 'notifyWhenChangesStop' } }); + this.isVisible = ko.computed({ + read: function () { + var visible = false; + var hasVisibleSubcategories = false; + _.forEach(self.subcategories, function (category) { + if (category.isVisible()) { + hasVisibleSubcategories = true; + return false; + } + }); + //Hide it if not inside the selected category. + var isInSelectedCategory = false, temp = self; + while (temp !== null) { + if (temp.isSelected()) { + isInSelectedCategory = true; + break; + } + temp = temp.parent; + } + //In single-category view, the category also counts as "selected" + //if one of its duplicates is selected. + if (!isInSelectedCategory + && (self.duplicates.length > 0) + && (editor.categoryViewMode() === RexRoleEditor.singleCategoryView)) { + for (var i = 0; i < self.duplicates.length; i++) { + temp = self.duplicates[i]; + while (temp !== null) { + if (temp.isSelected()) { + isInSelectedCategory = true; + break; + } + temp = temp.parent; + } + if (isInSelectedCategory) { + break; + } + } + } + if (!isInSelectedCategory && !hasVisibleSubcategories) { + return false; + } + //Stay visible as long as at least one subcategory or permission is visible. + visible = hasVisibleSubcategories; + _.forEach(self.permissions(), function (permission) { + if (permission.isVisible()) { + visible = true; + return false; + } + }); + return visible; + }, + deferEvaluation: true, + owner: this, + }); + this.isVisible.extend({ + rateLimit: { + timeout: 10, + method: 'notifyWhenChangesStop' + } + }); + this.desiredColumnCount = ko.computed({ + read: function () { + var visiblePermissions = 0; + _.forEach(self.permissions(), function (permission) { + if (permission.isVisible()) { + visiblePermissions++; + } + }); + var minItemsPerColumn = 12; + if (editor.categoryWidthMode() === 'full') { + minItemsPerColumn = 3; + } + var desiredColumns = Math.max(Math.ceil(visiblePermissions / minItemsPerColumn), 1); + //Avoid situations where the last column has only one item (an orphan). + if ((desiredColumns >= 2) && (visiblePermissions % minItemsPerColumn === 1)) { + desiredColumns--; + } + if (desiredColumns > 3) { + return 'max'; + } + return desiredColumns.toString(10); + }, + deferEvaluation: true + }); + this.nestingDepth = ko.pureComputed({ + read: function () { + if (self.parent !== null) { + return self.parent.nestingDepth() + 1; + } + return 1; + }, + deferEvaluation: true + }); + this.isNavExpanded = ko.observable((this.slug !== null) ? !editor.userPreferences.collapsedCategories.peek(this.slug) : true); + if (this.slug) { + this.isNavExpanded.subscribe(function (newValue) { + editor.userPreferences.collapsedCategories.toggle(_this.slug, !newValue); + }); + } + this.isNavVisible = ko.pureComputed({ + read: function () { + if (self.parent === null) { + return true; + } + return self.parent.isNavVisible() && self.parent.isNavExpanded(); + //Idea: We could hide it if all of the capabilities it contains have been deleted. + }, + deferEvaluation: true + }); + this.cssClasses = ko.computed({ + read: function () { + var classes = []; + if (self.subcategories.length > 0) { + classes.push('rex-has-subcategories'); + } + if (self.parent) { + if (self.parent === editor.rootCategory) { + classes.push('rex-top-category'); + } + else { + classes.push('rex-sub-category'); + } + } + if (self.permissions().length > 0) { + classes.push('rex-desired-columns-' + self.desiredColumnCount()); + } + return classes.join(' '); + }, + deferEvaluation: true + }); + this.navCssClasses = ko.pureComputed({ + read: function () { + var classes = []; + if (self.isSelected()) { + classes.push('rex-selected-nav-item'); + } + if (self.isNavExpanded()) { + classes.push('rex-nav-is-expanded'); + } + if (self.subcategories.length > 0) { + classes.push('rex-nav-has-children'); + } + classes.push('rex-nav-level-' + self.nestingDepth()); + return classes.join(' '); + }, + deferEvaluation: true + }); + this.subcategoryModificationFlag = ko.observable(this.subcategories.length); + this.sortedSubcategories = ko.pureComputed({ + read: function () { + //Refresh the sorted list when categories are added or removed. + _this.subcategoryModificationFlag(); + return _this.getSortedSubcategories(); + }, + deferEvaluation: true + }); + this.navSubcategories = ko.pureComputed({ + read: function () { + _this.subcategoryModificationFlag(); + return _this.subcategories; + }, + deferEvaluation: true + }); + this.subheading = ko.pureComputed({ + read: function () { + return _this.getSubheadingItems().join(', '); + }, + deferEvaluation: true + }); + } + RexCategory.prototype.addSubcategory = function (category, afterName) { + category.parent = this; + if (afterName) { + var index = wsAmeLodash.findIndex(this.subcategories, { 'name': afterName }); + if (index > -1) { + this.subcategories.splice(index + 1, 0, category); + this.subcategoryModificationFlag(this.subcategories.length); + return; + } + } + this.subcategories.push(category); + this.subcategoryModificationFlag(this.subcategories.length); + }; + // noinspection JSUnusedGlobalSymbols Used in KO templates. + RexCategory.prototype.toggleSubcategories = function () { + this.isNavExpanded(!this.isNavExpanded()); + }; + RexCategory.prototype.getSortedSubcategories = function () { + //In most cases, the subcategory list is already sorted either alphabetically or in a predefined order + //chosen for specific category. Subcategories can override this method to change the sort order. + return this.subcategories; + }; + /** + * Sort the permissions in this category. Doesn't affect subcategories. + * The default sort is alphabetical, but subclasses can override this method to specify a custom order. + */ + RexCategory.prototype.sortPermissions = function () { + this.permissions.sort(function (a, b) { + return a.capability.name.toLowerCase().localeCompare(b.capability.name.toLowerCase()); + }); + }; + RexCategory.prototype.countUniqueCapabilities = function (accumulator, predicate) { + if (accumulator === void 0) { accumulator = {}; } + if (predicate === void 0) { predicate = null; } + var total = 0; + var permissions = this.permissions(); + for (var i = 0; i < permissions.length; i++) { + var capability = permissions[i].capability; + if (accumulator.hasOwnProperty(capability.name)) { + continue; + } + if (predicate && !predicate(capability)) { + continue; + } + if (capability.isDeleted()) { + continue; + } + accumulator[capability.name] = true; + total++; + } + for (var i = 0; i < this.subcategories.length; i++) { + total = total + this.subcategories[i].countUniqueCapabilities(accumulator, predicate); + } + return total; + }; + RexCategory.prototype.findCategoryBySlug = function (slug) { + if (this.editor.categoriesBySlug.hasOwnProperty(slug)) { + return this.editor.categoriesBySlug[slug]; + } + return null; + }; + RexCategory.fromJs = function (details, editor) { + var category; + if (details.variant && details.variant === 'post_type') { + category = new RexPostTypeCategory(details.name, editor, details.contentTypeId, details.slug, details.permissions); + } + else if (details.variant && details.variant === 'taxonomy') { + category = new RexTaxonomyCategory(details.name, editor, details.contentTypeId, details.slug, details.permissions); + } + else { + category = new RexCategory(details.name, editor, details.slug, details.capabilities); + } + if (details.componentId) { + category.origin = editor.getComponent(details.componentId); + } + if (details.subcategories) { + wsAmeLodash.forEach(details.subcategories, function (childDetails) { + var subcategory = RexCategory.fromJs(childDetails, editor); + category.addSubcategory(subcategory); + }); + } + return category; + }; + RexCategory.prototype.usesBaseCapabilities = function () { + return false; + }; + RexCategory.prototype.getDeDuplicationKey = function () { + var _a; + var key = (_a = this.slug) !== null && _a !== void 0 ? _a : this.name; + if (this.parent) { + key = this.parent.getDeDuplicationKey() + '>' + key; + } + return key; + }; + RexCategory.prototype.addDuplicate = function (category) { + if (this.duplicates.indexOf(category) === -1) { + this.duplicates.push(category); + } + }; + RexCategory.prototype.getSubheadingItems = function () { + var items = []; + if (this.parent !== null) { + items.push(this.parent.name); + } + if (this.duplicates.length > 0) { + for (var i = 0; i < this.duplicates.length; i++) { + var category = this.duplicates[i]; + if (category.parent) { + items.push(category.parent.name); + } + } + } + return items; + }; + RexCategory.prototype.getAbsoluteName = function () { + var components = [this.name]; + var parent = this.parent; + while (parent !== null) { + components.unshift(parent.name); + parent = parent.parent; + } + return components.join(' > '); + }; + RexCategory.defaultSubcategoryComparison = function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }; + return RexCategory; +}()); +var RexContentTypeCategory = /** @class */ (function (_super) { + __extends(RexContentTypeCategory, _super); + function RexContentTypeCategory(name, editor, slug) { + if (slug === void 0) { slug = null; } + var _this = _super.call(this, name, editor, slug) || this; + _this.actions = {}; + _this.baseCategorySlug = null; + _this.isBaseCapNoticeVisible = ko.pureComputed({ + read: function () { + if (editor.showBaseCapsEnabled()) { + return false; + } + return _this.usesBaseCapabilities(); + }, + deferEvaluation: true + }); + return _this; + } + /** + * Check if the post type or taxonomy represented by this category uses the same capabilities + * as the built-in "post" type or the "category" taxonomy. + */ + RexContentTypeCategory.prototype.usesBaseCapabilities = function () { + var baseCategory = this.getBaseCategory(); + if (baseCategory === null || this === baseCategory) { + return false; + } + var allCapsMatch = true; + wsAmeLodash.forEach(this.actions, function (item) { + var isMatch = item.action + && baseCategory.actions.hasOwnProperty(item.action) + && (item.capability === baseCategory.actions[item.action].capability); + if (!isMatch) { + allCapsMatch = false; + return false; + } + }); + return allCapsMatch; + }; + RexContentTypeCategory.prototype.getBaseCategory = function () { + if (this.baseCategorySlug !== null) { + var result = this.findCategoryBySlug(this.baseCategorySlug); + if (result instanceof RexContentTypeCategory) { + return result; + } + } + return null; + }; + return RexContentTypeCategory; +}(RexCategory)); +var RexPostTypePermission = /** @class */ (function (_super) { + __extends(RexPostTypePermission, _super); + function RexPostTypePermission(editor, capability, action, pluralNoun) { + if (pluralNoun === void 0) { pluralNoun = ''; } + var _this = _super.call(this, editor, capability) || this; + _this.action = action; + _this.readableAction = wsAmeLodash.capitalize(_this.action.replace('_posts', '').replace('_', ' ')); + if (RexPostTypePermission.actionDescriptions.hasOwnProperty(action) && pluralNoun) { + _this.mainDescription = RexPostTypePermission.actionDescriptions[action].replace('%s', pluralNoun); + } + return _this; + } + RexPostTypePermission.actionDescriptions = { + 'edit_and_create': 'Edit and create %s', + 'edit_posts': 'Edit %s', + 'create_posts': 'Create new %s', + 'edit_published_posts': 'Edit published %s', + 'edit_others_posts': 'Edit %s created by others', + 'edit_private_posts': 'Edit private %s created by others', + 'publish_posts': 'Publish %s', + 'read_private_posts': 'Read private %s', + 'delete_posts': 'Delete %s', + 'delete_published_posts': 'Delete published %s', + 'delete_others_posts': 'Delete %s created by others', + 'delete_private_posts': 'Delete private %s created by others', + }; + return RexPostTypePermission; +}(RexPermission)); +var RexPostTypeCategory = /** @class */ (function (_super) { + __extends(RexPostTypeCategory, _super); + function RexPostTypeCategory(name, editor, postTypeId, slug, permissions, isDefault) { + if (slug === void 0) { slug = null; } + if (isDefault === void 0) { isDefault = false; } + var _this = _super.call(this, name, editor, slug) || this; + _this.pluralLabel = ''; + _this.actions = {}; + var _ = wsAmeLodash; + _this.baseCategorySlug = 'postTypes/post'; + _this.postType = postTypeId; + _this.isDefault = isDefault; + _this.subtitle = _this.postType; + if (editor.postTypes[postTypeId].pluralLabel) { + _this.pluralLabel = editor.postTypes[postTypeId].pluralLabel; + } + else { + _this.pluralLabel = name.toLowerCase(); + } + _this.permissions = ko.observableArray(_.map(permissions, function (capability, action) { + var permission = new RexPostTypePermission(editor, editor.getCapability(capability), action, _this.pluralLabel); + //The "read" capability is already shown in the core category and every role has it by default. + if (capability === 'read') { + permission.isRedundant = true; + } + _this.actions[action] = permission; + return permission; + })); + _this.sortPermissions(); + //The "create" capability is often the same as the "edit" capability. + var editPerm = _.get(_this.actions, 'edit_posts', null), createPerm = _.get(_this.actions, 'create_posts', null); + if (editPerm && createPerm && (createPerm.capability.name === editPerm.capability.name)) { + createPerm.isRedundant = true; + } + return _this; + } + RexPostTypeCategory.prototype.getDeDuplicationKey = function () { + return 'postType:' + this.postType; + }; + RexPostTypeCategory.prototype.sortPermissions = function () { + this.permissions.sort(function (a, b) { + var priorityA = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexPostTypeCategory.desiredActionOrder[a.action] : 1000; + var priorityB = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexPostTypeCategory.desiredActionOrder[b.action] : 1000; + var delta = priorityA - priorityB; + if (delta !== 0) { + return delta; + } + return a.capability.name.localeCompare(b.capability.name); + }); + }; + RexPostTypeCategory.prototype.getSubheadingItems = function () { + var items = _super.prototype.getSubheadingItems.call(this); + items.push(this.postType); + return items; + }; + RexPostTypeCategory.desiredActionOrder = { + 'edit_posts': 1, + 'edit_others_posts': 2, + 'edit_published_posts': 3, + 'edit_private_posts': 4, + 'publish_posts': 5, + 'delete_posts': 6, + 'delete_others_posts': 7, + 'delete_published_posts': 8, + 'delete_private_posts': 9, + 'read_private_posts': 10, + 'create_posts': 11, + }; + return RexPostTypeCategory; +}(RexContentTypeCategory)); +var RexTaxonomyPermission = /** @class */ (function (_super) { + __extends(RexTaxonomyPermission, _super); + function RexTaxonomyPermission(editor, capability, action, pluralNoun) { + if (pluralNoun === void 0) { pluralNoun = ''; } + var _this = _super.call(this, editor, capability) || this; + _this.action = action; + _this.readableAction = wsAmeLodash.capitalize(_this.action.replace('_terms', '').replace('_', ' ')); + if (RexTaxonomyPermission.actionDescriptions.hasOwnProperty(action) && pluralNoun) { + _this.mainDescription = RexTaxonomyPermission.actionDescriptions[action].replace('%s', pluralNoun); + } + return _this; + } + RexTaxonomyPermission.actionDescriptions = { + 'manage_terms': 'Manage %s', + 'edit_terms': 'Edit %s', + 'delete_terms': 'Delete %s', + 'assign_terms': 'Assign %s', + }; + return RexTaxonomyPermission; +}(RexPermission)); +var RexTaxonomyCategory = /** @class */ (function (_super) { + __extends(RexTaxonomyCategory, _super); + function RexTaxonomyCategory(name, editor, taxonomyId, slug, permissions) { + if (slug === void 0) { slug = null; } + var _this = _super.call(this, name, editor, slug) || this; + _this.actions = {}; + var _ = wsAmeLodash; + _this.baseCategorySlug = 'taxonomies/category'; + _this.taxonomy = taxonomyId; + _this.subtitle = taxonomyId; + var noun = name.toLowerCase(); + _this.permissions = ko.observableArray(_.map(permissions, function (capability, action) { + var permission = new RexTaxonomyPermission(editor, editor.getCapability(capability), action, noun); + _this.actions[action] = permission; + return permission; + })); + _this.sortPermissions(); + //Permissions that use the same capability as the "manage_terms" permission are redundant. + if (_this.actions.manage_terms) { + var manageCap = _this.actions.manage_terms.capability.name; + for (var action in _this.actions) { + if (!_this.actions.hasOwnProperty(action)) { + continue; + } + if ((action !== 'manage_terms') && (_this.actions[action].capability.name === manageCap)) { + _this.actions[action].isRedundant = true; + } + } + } + return _this; + } + RexTaxonomyCategory.prototype.getDeDuplicationKey = function () { + return 'taxonomy:' + this.taxonomy; + }; + RexTaxonomyCategory.prototype.sortPermissions = function () { + this.permissions.sort(function (a, b) { + var priorityA = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexTaxonomyCategory.desiredActionOrder[a.action] : 1000; + var priorityB = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexTaxonomyCategory.desiredActionOrder[b.action] : 1000; + var delta = priorityA - priorityB; + if (delta !== 0) { + return delta; + } + return a.capability.name.localeCompare(b.capability.name); + }); + }; + RexTaxonomyCategory.prototype.getSubheadingItems = function () { + var items = _super.prototype.getSubheadingItems.call(this); + items.push(this.taxonomy); + return items; + }; + RexTaxonomyCategory.desiredActionOrder = { + 'manage_terms': 1, + 'edit_terms': 2, + 'delete_terms': 3, + 'assign_terms': 4, + }; + return RexTaxonomyCategory; +}(RexContentTypeCategory)); +var RexTableViewCategory = /** @class */ (function (_super) { + __extends(RexTableViewCategory, _super); + function RexTableViewCategory(name, editor, slug) { + if (slug === void 0) { slug = null; } + var _this = _super.call(this, name, editor, slug) || this; + _this.subcategoryComparisonCallback = null; + _this.contentTemplate = ko.pureComputed(function () { + if (editor.categoryViewMode() === RexRoleEditor.hierarchyView) { + return 'rex-permission-table-template'; + } + return 'rex-default-category-content-template'; + }); + _this.subcategoryComparisonCallback = RexCategory.defaultSubcategoryComparison; + return _this; + } + RexTableViewCategory.prototype.getSortedSubcategories = function () { + var _this = this; + if (this.editor.showBaseCapsEnabled()) { + return _super.prototype.getSortedSubcategories.call(this); + } + var cats = wsAmeLodash.clone(this.subcategories); + return cats.sort(function (a, b) { + //Special case: Put categories that use base capabilities at the end. + var aEqualsBase = a.usesBaseCapabilities(); + var bEqualsBase = b.usesBaseCapabilities(); + if (aEqualsBase && !bEqualsBase) { + return 1; + } + else if (!aEqualsBase && bEqualsBase) { + return -1; + } + //Otherwise just sort in the default order. + return _this.subcategoryComparisonCallback(a, b); + }); + }; + /** + * Sort the underlying category array. + */ + RexTableViewCategory.prototype.sortSubcategories = function () { + this.subcategories.sort(this.subcategoryComparisonCallback); + }; + return RexTableViewCategory; +}(RexCategory)); +var RexTaxonomyContainerCategory = /** @class */ (function (_super) { + __extends(RexTaxonomyContainerCategory, _super); + function RexTaxonomyContainerCategory(name, editor, slug) { + if (slug === void 0) { slug = null; } + var _this = _super.call(this, name, editor, slug) || this; + _this.htmlId = 'rex-taxonomy-summary-category'; + _this.tableColumns = ko.pureComputed({ + read: function () { + var _ = wsAmeLodash; + var defaultTaxonomyActions = ['manage_terms', 'assign_terms', 'edit_terms', 'delete_terms']; + var columns = [ + { + title: 'Manage', + actions: ['manage_terms'] + }, + { + title: 'Assign', + actions: ['assign_terms'] + }, + { + title: 'Edit', + actions: ['edit_terms'] + }, + { + title: 'Delete', + actions: ['delete_terms'] + } + ]; + var misColumnExists = false, miscColumn = null; + for (var i = 0; i < _this.subcategories.length; i++) { + var category = _this.subcategories[i]; + if (!(category instanceof RexTaxonomyCategory)) { + continue; + } + //Display any unrecognized actions in a "Misc" column. + var customActions = _.omit(category.actions, defaultTaxonomyActions); + if (!_.isEmpty(customActions)) { + if (!misColumnExists) { + miscColumn = { title: 'Misc', actions: [] }; + columns.push(miscColumn); + } + miscColumn.actions = _.union(miscColumn.actions, _.keys(customActions)); + } + } + return columns; + }, + deferEvaluation: true, + }); + return _this; + } + return RexTaxonomyContainerCategory; +}(RexTableViewCategory)); +var RexPostTypeContainerCategory = /** @class */ (function (_super) { + __extends(RexPostTypeContainerCategory, _super); + function RexPostTypeContainerCategory(name, editor, slug) { + if (slug === void 0) { slug = null; } + var _this = _super.call(this, name, editor, slug) || this; + /* Note: This seems like poor design because the superclass overrides subclass + * behaviour (subcategory comparison) in some situations. Unfortunately, I haven't + * come up with anything better so far. Might be something to revisit later. + */ + _this.subcategoryComparisonCallback = function (a, b) { + //Special case: Put "Posts" at the top. + if (a.postType === 'post') { + return -1; + } + else if (b.postType === 'post') { + return 1; + } + //Put other built-in post types above custom post types. + if (a.isDefault && !b.isDefault) { + return -1; + } + else if (b.isDefault && !a.isDefault) { + return 1; + } + var labelA = a.name.toLowerCase(), labelB = b.name.toLowerCase(); + return labelA.localeCompare(labelB); + }; + _this.tableColumns = ko.pureComputed({ + read: function () { + var _ = wsAmeLodash; + var defaultPostTypeActions = _.keys(RexPostTypePermission.actionDescriptions); + var columns = [ + { + title: 'Own items', + actions: ['create_posts', 'edit_posts', 'delete_posts', 'publish_posts', 'edit_published_posts', 'delete_published_posts'] + }, + { + title: 'Other\'s items', + actions: ['edit_others_posts', 'delete_others_posts', 'edit_private_posts', 'delete_private_posts', 'read_private_posts'] + } + ]; + var metaColumn = { + title: 'Meta', + actions: ['edit_post', 'delete_post', 'read_post'] + }; + columns.push(metaColumn); + for (var i = 0; i < _this.subcategories.length; i++) { + var category = _this.subcategories[i]; + if (!(category instanceof RexPostTypeCategory)) { + continue; + } + //Display any unrecognized actions in a "Misc" column. + var customActions = _.omit(category.actions, defaultPostTypeActions); + if (!_.isEmpty(customActions)) { + metaColumn.actions = _.union(metaColumn.actions, _.keys(customActions)); + } + } + return columns; + }, + deferEvaluation: true, + }); + return _this; + } + return RexPostTypeContainerCategory; +}(RexTableViewCategory)); +var RexCapability = /** @class */ (function () { + function RexCapability(name, editor) { + var _this = this; + this.originComponent = null; + this.usedByComponents = []; + this.menuItems = []; + this.usedByPostTypeActions = {}; + this.usedByTaxonomyActions = {}; + this.predefinedPermissions = []; + this.documentationUrl = null; + this.notes = null; + this.name = String(name); + this.editor = editor; + var self = this; + this.readableName = wsAmeLodash.capitalize(this.name.replace(/[_\-\s]+/g, ' ')); + this.displayName = ko.pureComputed({ + read: function () { + return editor.readableNamesEnabled() ? self.readableName : self.name; + }, + deferEvaluation: true, + owner: this + }); + this.isDeleted = ko.observable(false); + this.responsibleActors = ko.computed({ + read: function () { + var actor = editor.selectedActor(), list = []; + if (actor instanceof RexUser) { + actor.hasCap(self.name, list); + } + return list; + }, + owner: this, + deferEvaluation: true + }); + this.isInherited = ko.computed({ + read: function () { + var actor = editor.selectedActor(); + if (!actor.canHaveRoles) { + return false; + } + var responsibleActors = self.responsibleActors(); + return responsibleActors + && (responsibleActors.length > 0) + && (wsAmeLodash.indexOf(responsibleActors, actor) < (responsibleActors.length - 1)); + }, + owner: this, + deferEvaluation: true + }); + this.isPersonalOverride = ko.pureComputed({ + read: function () { + //This flag applies only to actors that can inherit permissions. + var actor = editor.selectedActor(); + if (!actor.canHaveRoles) { + return false; + } + return !self.isInherited(); + }, + owner: this, + deferEvaluation: true + }); + this.isEditable = ko.pureComputed({ + read: function () { + if (self.isInherited() && !editor.inheritanceOverrideEnabled()) { + return false; + } + return !self.isDeleted(); + }, + deferEvaluation: true + }); + this.isEnabledForSelectedActor = ko.computed({ + read: function () { + return editor.selectedActor().hasCap(self.name); + }, + write: function (newState) { + var actor = editor.selectedActor(); + if (editor.isShiftKeyDown()) { + //Hold the shift key while clicking to cycle the capability between 3 states: + //Granted -> Denied -> Not granted. + var oldState = actor.getOwnCapabilityState(self.name); + if (newState) { + if (oldState === false) { + actor.deleteCap(self.name); //Denied -> Not granted. + } + else if (oldState === null) { + actor.setCap(self.name, true); //Not granted -> Granted. + } + } + else { + if (oldState === true) { + actor.setCap(self.name, false); //Granted -> Denied. + } + else if (oldState === null) { + actor.setCap(self.name, true); //Not granted (inherited = Granted) -> Granted. + } + } + //Update the checkbox state. + if (actor.hasCap(self.name) !== newState) { + self.isEnabledForSelectedActor.notifySubscribers(); + } + return; + } + if (newState) { + //TODO: If it's a user and the cap is explicitly negated, consider removing that state. + actor.setCap(self.name, newState); + } + else { + //The default is to remove the capability instead of explicitly setting it to false. + actor.deleteCap(self.name); + //If we're removing a capability from a user but one of their roles also has it, + //we have to set it to false after all or it will stay enabled. + if (actor.canHaveRoles && actor.hasCap(self.name)) { + actor.setCap(self.name, newState); + } + } + }, + owner: this, + deferEvaluation: true + }); + //this.isEnabledForSelectedActor.extend({rateLimit: {timeout: 10, method: "notifyWhenChangesStop"}}); + this.isExplicitlyDenied = ko.pureComputed({ + read: function () { + var actor = editor.selectedActor(); + if (actor) { + return (actor.getCapabilityState(self.name) === false); + } + return false; + }, + deferEvaluation: true + }); + this.grantedPermissions = ko.computed({ + read: function () { + var _ = wsAmeLodash; + var results = []; + if (_this.predefinedPermissions.length > 0) { + results = _this.predefinedPermissions.slice(); + } + function localeAwareCompare(a, b) { + return a.localeCompare(b); + } + function actionsToPermissions(actionGroups, labelMap, descriptions) { + return _.map(actionGroups, function (ids, action) { + var labels = _.map(ids, function (id) { return labelMap[id].pluralLabel; }) + .sort(localeAwareCompare); + var template = descriptions[action]; + if (!template) { + template = action + ': %s'; + } + return template.replace('%s', RexCapability.formatNounList(labels)); + }).sort(localeAwareCompare); + } + //Post permissions. + var postActionGroups = _.transform(_this.usedByPostTypeActions, function (accumulator, actions, postType) { + var actionKeys = _.keys(actions); + //Combine "edit" and "create" permissions because they usually use the same capability. + var editEqualsCreate = actions.hasOwnProperty('edit_posts') && actions.hasOwnProperty('create_posts'); + if (editEqualsCreate) { + actionKeys = _.without(actionKeys, 'edit_posts', 'create_posts'); + actionKeys.unshift('edit_and_create'); + } + _.forEach(actionKeys, function (action) { + if (!accumulator.hasOwnProperty(action)) { + accumulator[action] = []; + } + accumulator[action].push(postType); + }); + }, {}); + var postPermissions = actionsToPermissions(postActionGroups, _this.editor.postTypes, RexPostTypePermission.actionDescriptions); + Array.prototype.push.apply(results, postPermissions); + //Taxonomy permissions. + var taxonomyActionGroups = _.transform(_this.usedByTaxonomyActions, function (accumulator, actions, taxonomy) { + var actionKeys = _.keys(actions); + //Most taxonomies use the same capability for manage_terms, edit_terms, and delete_terms. + //In those cases, let's show only manage_terms. + if (actions.hasOwnProperty('manage_terms')) { + actionKeys = _.without(actionKeys, 'edit_terms', 'delete_terms'); + } + _.forEach(actionKeys, function (action) { + if (!accumulator.hasOwnProperty(action)) { + accumulator[action] = []; + } + accumulator[action].push(taxonomy); + }); + }, {}); + var taxonomyPermissions = actionsToPermissions(taxonomyActionGroups, _this.editor.taxonomies, RexTaxonomyPermission.actionDescriptions); + Array.prototype.push.apply(results, taxonomyPermissions); + Array.prototype.push.apply(results, _this.menuItems); + return results; + }, + deferEvaluation: true, + owner: this, + }); + } + // noinspection JSUnusedGlobalSymbols Used in KO templates. + RexCapability.prototype.getDocumentationUrl = function () { + if (this.documentationUrl) { + return this.documentationUrl; + } + if (this.originComponent && this.originComponent.capabilityDocumentationUrl) { + this.documentationUrl = this.originComponent.capabilityDocumentationUrl; + return this.documentationUrl; + } + return null; + }; + RexCapability.fromJs = function (name, data, editor) { + var capability = new RexCapability(name, editor); + capability.menuItems = data.menuItems.sort(function (a, b) { + return a.localeCompare(b); + }); + if (data.componentId) { + capability.originComponent = editor.getComponent(data.componentId); + } + if (data.usedByComponents) { + for (var id in data.usedByComponents) { + var component = editor.getComponent(id); + if (component) { + capability.usedByComponents.push(component); + } + } + } + if (data.documentationUrl) { + capability.documentationUrl = data.documentationUrl; + } + if (data.permissions && (data.permissions.length > 0)) { + capability.predefinedPermissions = data.permissions; + } + if ((capability.originComponent === editor.coreComponent) && (capability.documentationUrl === null)) { + capability.documentationUrl = 'https://wordpress.org/support/article/roles-and-capabilities/#' + + encodeURIComponent(capability.name); + } + if (data.readableName) { + capability.readableName = data.readableName; + } + return capability; + }; + RexCapability.formatNounList = function (items) { + if (items.length <= 2) { + return items.join(' and '); + } + return items.slice(0, -1).join(', ') + ', and ' + items[items.length - 1]; + }; + return RexCapability; +}()); +var RexDoNotAllowCapability = /** @class */ (function (_super) { + __extends(RexDoNotAllowCapability, _super); + function RexDoNotAllowCapability(editor) { + var _this = _super.call(this, 'do_not_allow', editor) || this; + _this.notes = '"do_not_allow" is a special capability. ' + + 'WordPress uses it internally to indicate that access is denied. ' + + 'Normally, it should not be assigned to any roles or users.'; + //Normally, it's impossible to grant this capability to anyone. Doing so would break things. + //However, if it's already granted, you can remove it. + _this.isEditable = ko.computed(function () { + return _this.isEnabledForSelectedActor(); + }); + return _this; + } + return RexDoNotAllowCapability; +}(RexCapability)); +var RexExistCapability = /** @class */ (function (_super) { + __extends(RexExistCapability, _super); + function RexExistCapability(editor) { + var _this = _super.call(this, 'exist', editor) || this; + _this.notes = '"exist" is a special capability. ' + + 'WordPress uses it internally to indicate that a role or user exists. ' + + 'Normally, everyone has this capability by default, and it is not necessary ' + + '(or possible) to assign it directly.'; + //Everyone must have this capability. However, if it has somehow become disabled, + //we'll let the user enable it. + _this.isEditable = ko.computed(function () { + return !_this.isEnabledForSelectedActor(); + }); + return _this; + } + return RexExistCapability; +}(RexCapability)); +var RexInvalidCapability = /** @class */ (function (_super) { + __extends(RexInvalidCapability, _super); + function RexInvalidCapability(fakeName, value, editor) { + var _this = _super.call(this, fakeName, editor) || this; + var startsWithVowel = /^[aeiou]/i; + var theType = (typeof value); + var nounPhrase = (startsWithVowel.test(theType) ? 'an' : 'a') + ' ' + theType; + _this.notes = 'This is not a valid capability. A capability name must be a string (i.e. text),' + + ' but this is ' + nounPhrase + '. It was probably created by a bug in another plugin or theme.'; + _this.isEditable = ko.computed(function () { + return false; + }); + return _this; + } + return RexInvalidCapability; +}(RexCapability)); +var RexUserPreferences = /** @class */ (function () { + function RexUserPreferences(initialPreferences, ajaxUrl, updateNonce) { + var _this = this; + var _ = wsAmeLodash; + initialPreferences = initialPreferences || {}; + if (_.isArray(initialPreferences)) { + initialPreferences = {}; + } + this.preferenceObservables = _.mapValues(initialPreferences, ko.observable, ko); + this.preferenceCount = ko.observable(_.size(this.preferenceObservables)); + this.collapsedCategories = new RexCollapsedCategorySet(_.get(initialPreferences, 'collapsedCategories', [])); + this.plainPreferences = ko.computed(function () { + //By creating a dependency on the number of preferences, we ensure that the observable will be re-evaluated + //whenever a preference is added or removed. + _this.preferenceCount(); + //This converts preferences to a plain JS object and establishes dependencies on all individual observables. + var result = _.mapValues(_this.preferenceObservables, function (observable) { + return observable(); + }); + result.collapsedCategories = _this.collapsedCategories.toJs(); + return result; + }); + //Avoid excessive AJAX requests. + this.plainPreferences.extend({ rateLimit: { timeout: 5000, method: "notifyWhenChangesStop" } }); + //Save preferences when they change. + if (ajaxUrl && updateNonce) { + this.plainPreferences.subscribe(function (preferences) { + //console.info('Saving user preferences', preferences); + jQuery.post(ajaxUrl, { + action: 'ws_ame_rex_update_user_preferences', + _ajax_nonce: updateNonce, + preferences: ko.toJSON(preferences) + }); + }); + } + } + RexUserPreferences.prototype.getObservable = function (name, defaultValue) { + if (defaultValue === void 0) { defaultValue = null; } + if (this.preferenceObservables.hasOwnProperty(name)) { + return this.preferenceObservables[name]; + } + var newPreference = ko.observable(defaultValue || null); + this.preferenceObservables[name] = newPreference; + this.preferenceCount(this.preferenceCount() + 1); + return newPreference; + }; + return RexUserPreferences; +}()); +/** + * An observable collection of unique strings. In this case, they're category slugs. + */ +var RexCollapsedCategorySet = /** @class */ (function () { + function RexCollapsedCategorySet(items) { + if (items === void 0) { items = []; } + this.isItemInSet = {}; + items = wsAmeLodash.uniq(items); + for (var i = 0; i < items.length; i++) { + this.isItemInSet[items[i]] = ko.observable(true); + } + this.items = ko.observableArray(items); + } + RexCollapsedCategorySet.prototype.getItemObservable = function (item) { + if (!this.isItemInSet.hasOwnProperty(item)) { + this.isItemInSet[item] = ko.observable(false); + } + return this.isItemInSet[item]; + }; + RexCollapsedCategorySet.prototype.add = function (item) { + if (!this.contains(item)) { + this.getItemObservable(item)(true); + this.items.push(item); + } + }; + RexCollapsedCategorySet.prototype.remove = function (item) { + if (this.contains(item)) { + this.isItemInSet[item](false); + this.items.remove(item); + } + }; + RexCollapsedCategorySet.prototype.toggle = function (item, addToSet) { + if (addToSet) { + this.add(item); + } + else { + this.remove(item); + } + }; + RexCollapsedCategorySet.prototype.contains = function (item) { + return this.getItemObservable(item)(); + }; + RexCollapsedCategorySet.prototype.peek = function (item) { + if (!this.isItemInSet.hasOwnProperty(item)) { + return false; + } + return this.isItemInSet[item].peek(); + }; + RexCollapsedCategorySet.prototype.toJs = function () { + return this.items(); + }; + return RexCollapsedCategorySet; +}()); +var RexBaseDialog = /** @class */ (function () { + function RexBaseDialog() { + var _this = this; + this.isOpen = ko.observable(false); + this.isRendered = ko.observable(false); + this.title = null; + this.options = { + buttons: [] + }; + this.isOpen.subscribe(function (isOpenNow) { + if (isOpenNow && !_this.isRendered()) { + _this.isRendered(true); + } + }); + } + RexBaseDialog.prototype.setupValidationTooltip = function (inputSelector, message) { + //Display validation messages next to the input field. + var element = this.jQueryWidget.find(inputSelector).qtip({ + overwrite: false, + content: '(Validation errors will appear here.)', + //Show the tooltip when the input is focused. + show: { + event: '', + ready: false, + effect: false + }, + hide: { + event: '', + effect: false + }, + position: { + my: 'center left', + at: 'center right', + effect: false + }, + style: { + classes: 'qtip-bootstrap qtip-shadow rex-tooltip' + } + }); + message.subscribe(function (newMessage) { + if (newMessage == '') { + element.qtip('option', 'content.text', 'OK'); + element.qtip('option', 'show.event', ''); + element.qtip('hide'); + } + else { + element.qtip('option', 'content.text', newMessage); + element.qtip('option', 'show.event', 'focus'); + element.qtip('show'); + } + }); + //Hide the tooltip when the dialog is closed and prevent it from automatically re-appearing. + this.isOpen.subscribe(function (isDialogOpen) { + if (!isDialogOpen) { + element.qtip('option', 'show.event', ''); + element.qtip('hide'); + } + }); + }; + ; + return RexBaseDialog; +}()); +var RexDeleteCapDialog = /** @class */ (function (_super) { + __extends(RexDeleteCapDialog, _super); + function RexDeleteCapDialog(editor) { + var _this = _super.call(this) || this; + _this.options = { + buttons: [], + minWidth: 380 + }; + _this.wasEverOpen = ko.observable(false); + var _ = wsAmeLodash; + _this.options.buttons.push({ + text: 'Delete Capability', + 'class': 'button button-primary rex-delete-selected-caps', + click: function () { + var selectedCapabilities = _.chain(_this.deletableItems()) + .filter(function (item) { + return item.isSelected(); + }) + .pluck('capability') + .value(); + //Note: We could remove confirmation if we get an "Undo" feature. + var noun = (selectedCapabilities.length === 1) ? 'capability' : 'capabilities'; + var warning = 'Caution: Deleting capabilities could break plugins that use those capabilities. ' + + 'Delete ' + selectedCapabilities.length + ' ' + noun + '?'; + if (!confirm(warning)) { + return; + } + _this.isOpen(false); + editor.deleteCapabilities(selectedCapabilities); + alert(selectedCapabilities.length + ' capabilities deleted'); + }, + disabled: true + }); + _this.isOpen.subscribe(function (open) { + if (open && !_this.wasEverOpen()) { + _this.wasEverOpen(true); + } + }); + _this.deletableItems = ko.pureComputed({ + read: function () { + var wpCore = editor.getComponent(':wordpress:'); + return _.chain(editor.capabilities) + .filter(function (capability) { + if (capability.originComponent === wpCore) { + return false; + } + return !capability.isDeleted(); + }) + //Pre-populate part of the list when the dialog is closed to ensure it has a non-zero height. + .take(_this.wasEverOpen() ? 1000000 : 30) + .sortBy(function (capability) { + return capability.name.toLowerCase(); + }) + .map(function (capability) { + return { + 'capability': capability, + 'isSelected': ko.observable(false) + }; + }) + .value(); + }, + deferEvaluation: true + }); + _this.selectedItemCount = ko.pureComputed({ + read: function () { return _.filter(_this.deletableItems(), function (item) { + return item.isSelected(); + }).length; }, + deferEvaluation: true + }); + var deleteButtonText = ko.pureComputed({ + read: function () { + var count = _this.selectedItemCount(); + if (count <= 0) { + return 'Delete Capability'; + } + else { + if (count === 1) { + return 'Delete 1 Capability'; + } + else { + return ('Delete ' + count + ' Capabilities'); + } + } + }, + deferEvaluation: true + }); + deleteButtonText.subscribe(function (newText) { + _this.jQueryWidget + .closest('.ui-dialog') + .find('.ui-dialog-buttonset .button-primary .ui-button-text') + .text(newText); + }); + _this.isDeleteButtonEnabled = ko.pureComputed({ + read: function () { + return _this.selectedItemCount() > 0; + }, + deferEvaluation: true + }); + return _this; + } + RexDeleteCapDialog.prototype.onOpen = function () { + //Deselect all items when the dialog is opened. + var items = this.deletableItems(); + for (var i = 0; i < items.length; i++) { + if (items[i].isSelected()) { + items[i].isSelected(false); + } + } + }; + return RexDeleteCapDialog; +}(RexBaseDialog)); +var RexAddCapabilityDialog = /** @class */ (function (_super) { + __extends(RexAddCapabilityDialog, _super); + function RexAddCapabilityDialog(editor) { + var _this = _super.call(this) || this; + _this.autoCancelButton = true; + _this.options = { + minWidth: 380 + }; + _this.validationState = ko.observable(RexAddCapabilityDialog.states.empty); + _this.validationMessage = ko.observable(''); + var _ = wsAmeLodash; + _this.editor = editor; + var excludedCaps = ['do_not_allow', 'exist', 'customize']; + var newCapabilityName = ko.observable(''); + _this.capabilityName = ko.computed({ + read: function () { + return newCapabilityName(); + }, + write: function (value) { + value = _.trimRight(value); + //Validate and sanitize the capability name. + var state = _this.validationState, message = _this.validationMessage; + //WP API allows completely arbitrary capability names, but this plugin forbids some characters + //for sanity's sake and to avoid XSS. + var invalidCharacters = /[><&\r\n\t]/g; + //While all other characters are allowed, it's recommended to stick to alphanumerics, + //underscores and dashes. Spaces are also OK because some other plugins use them. + var suspiciousCharacters = /[^a-z0-9_ -]/ig; + //PHP doesn't allow numeric string keys, and there's no conceivable reason to start the name with a space. + var invalidFirstCharacter = /^[\s0-9]/i; + var foundInvalid = value.match(invalidCharacters); + var foundSuspicious = value.match(suspiciousCharacters); + if (foundInvalid !== null) { + state(RexAddCapabilityDialog.states.error); + message('Sorry, ' + _.escape(_.last(foundInvalid)) + '
is not allowed here.'); + } + else if (value.match(invalidFirstCharacter) !== null) { + state(RexAddCapabilityDialog.states.error); + message('Capability name should start with a letter or an underscore.'); + } + else if (editor.capabilityExists(value)) { + //Duplicates are not allowed. + state(RexAddCapabilityDialog.states.error); + message('That capability already exists.'); + } + else if (editor.getRole(value) !== null) { + state(RexAddCapabilityDialog.states.error); + message('Capability name can\'t be the same as the name of a role.'); + } + else if (excludedCaps.indexOf(value) >= 0) { + state(RexAddCapabilityDialog.states.error); + message('That is a meta capability or a reserved capability name.'); + } + else if (foundSuspicious !== null) { + state(RexAddCapabilityDialog.states.notice); + message('For best compatibility, we recommend using only English letters, numbers, and underscores.'); + } + else if (value === '') { + //Empty input, nothing to validate. + state(RexAddCapabilityDialog.states.empty); + message(''); + } + else { + state(RexAddCapabilityDialog.states.valid); + message(''); + } + newCapabilityName(value); + } + }); + var acceptableStates = [RexAddCapabilityDialog.states.valid, RexAddCapabilityDialog.states.notice]; + _this.isAddButtonEnabled = ko.pureComputed(function () { + return (acceptableStates.indexOf(_this.validationState()) >= 0); + }); + _this.options.buttons = [{ + text: 'Add Capability', + 'class': 'button button-primary', + click: function () { + _this.onConfirm(); + }, + disabled: true + }]; + return _this; + } + RexAddCapabilityDialog.prototype.onOpen = function (event, ui) { + //Clear the input when the dialog is opened. + this.capabilityName(''); + }; + RexAddCapabilityDialog.prototype.onConfirm = function () { + if (!this.isAddButtonEnabled()) { + return; + } + var category = this.editor.addCapability(this.capabilityName().trim()); + this.isOpen(false); + //Note: Maybe the user doesn't need this alert? Hmm. + if (!category || (this.editor.categoryViewMode() === RexRoleEditor.listView)) { + alert('Capability added'); + } + else { + alert('Capability added to the "' + category.getAbsoluteName() + '" category.'); + } + }; + RexAddCapabilityDialog.states = { + valid: 'valid', + empty: 'empty', + notice: 'notice', + error: 'error' + }; + return RexAddCapabilityDialog; +}(RexBaseDialog)); +var RexAddRoleDialog = /** @class */ (function (_super) { + __extends(RexAddRoleDialog, _super); + function RexAddRoleDialog(editor) { + var _this = _super.call(this) || this; + _this.roleName = ko.observable(''); + _this.roleDisplayName = ko.observable(''); + _this.roleToCopyFrom = ko.observable(null); + _this.nameValidationMessage = ko.observable(''); + _this.displayNameValidationMessage = ko.observable(''); + _this.areTooltipsInitialised = false; + var _ = wsAmeLodash; + _this.editor = editor; + _this.options.minWidth = 380; + _this.options.buttons.push({ + text: 'Add Role', + 'class': 'button button-primary', + click: _this.onConfirm.bind(_this), + disabled: true + }); + _this.roleDisplayName.extend({ rateLimit: 10 }); + _this.roleName.extend({ rateLimit: 10 }); + //Role names are restricted - you can only use lowercase Latin letters, numbers and underscores. + var roleNameCharacterGroup = 'a-z0-9_'; + var invalidCharacterRegex = new RegExp('[^' + roleNameCharacterGroup + ']', 'g'); + var numbersOnlyRegex = /^[0-9]+$/; + _this.isNameValid = ko.computed(function () { + var name = _this.roleName().trim(); + var message = _this.nameValidationMessage; + //Name must not be empty. + if (name === '') { + message(''); + return false; + } + //Name can only contain certain characters. + var invalidChars = name.match(invalidCharacterRegex); + if (invalidChars !== null) { + var lastInvalidChar = _.last(invalidChars); + if (lastInvalidChar === ' ') { + lastInvalidChar = 'space'; + } + message('Sorry,' + _.escape(lastInvalidChar) + '
is not allowed here.
' + + 'Please enter only lowercase English letters, numbers, and underscores.'); + return false; + } + //Numeric names could cause problems with how PHP handles associative arrays. + if (numbersOnlyRegex.test(name)) { + message('Numeric names are not allowed. Please add at least one letter or underscore.'); + return false; + } + //Name must not be a duplicate. + var existingRole = editor.getRole(name); + if (existingRole) { + message('Duplicate role name.'); + return false; + } + //WP stores capabilities and role names in the same associative array, + //so they must be unique with respect to each other. + if (editor.capabilityExists(name)) { + message('Role name can\'t be the same as a capability name.'); + return false; + } + message(''); + return true; + }); + _this.isDisplayNameValid = ko.computed(function () { + var name = _this.roleDisplayName(); + var message = _this.displayNameValidationMessage; + return RexAddRoleDialog.validateDisplayName(name, message); + }); + //Automatically generate a role name from the display name. Basically, turn it into a slug. + var lastAutoRoleName = null; + _this.roleDisplayName.subscribe(function (displayName) { + var slug = _.snakeCase(displayName); + //Use the auto-generated role name only if the user hasn't entered their own. + var currentValue = _this.roleName(); + if ((currentValue === '') || (currentValue === lastAutoRoleName)) { + _this.roleName(slug); + } + lastAutoRoleName = slug; + }); + _this.isAddButtonEnabled = ko.pureComputed({ + read: function () { + return (_this.roleName() !== '') && (_this.roleDisplayName() !== '') + && _this.isNameValid() && _this.isDisplayNameValid(); + }, + deferEvaluation: true + }); + return _this; + } + RexAddRoleDialog.validateDisplayName = function (name, validationMessage) { + name = name.trim(); + if (name === '') { + validationMessage(''); + return false; + } + //You can choose pretty much any display name you like, but we'll forbid special characters + //that might cause problems for plugins that don't escape output for HTML. + if (RexAddRoleDialog.invalidDisplayNameRegex.test(name)) { + validationMessage('Sorry, these characters are not allowed:< > &
'); + return false; + } + validationMessage(''); + return true; + }; + RexAddRoleDialog.prototype.onOpen = function (event, ui) { + //Clear dialog fields when it's opened. + this.roleName(''); + this.roleDisplayName(''); + this.roleToCopyFrom(null); + if (!this.areTooltipsInitialised) { + this.setupValidationTooltip('#rex-new-role-display-name', this.displayNameValidationMessage); + this.setupValidationTooltip('#rex-new-role-name', this.nameValidationMessage); + this.areTooltipsInitialised = true; + } + }; + RexAddRoleDialog.prototype.onConfirm = function () { + if (!this.isAddButtonEnabled()) { + return; + } + this.isOpen(false); + var caps = {}; + if (this.roleToCopyFrom()) { + caps = this.roleToCopyFrom().getOwnCapabilities(); + } + this.editor.addRole(this.roleName(), this.roleDisplayName(), caps); + }; + RexAddRoleDialog.invalidDisplayNameRegex = /[><&\r\n\t]/; + return RexAddRoleDialog; +}(RexBaseDialog)); +var RexDeleteRoleDialog = /** @class */ (function (_super) { + __extends(RexDeleteRoleDialog, _super); + function RexDeleteRoleDialog(editor) { + var _this = _super.call(this) || this; + _this.isRoleSelected = {}; + _this.editor = editor; + _this.options.minWidth = 420; + _this.options.buttons.push({ + text: 'Delete Role', + 'class': 'button button-primary', + click: _this.onConfirm.bind(_this), + disabled: true + }); + _this.isDeleteButtonEnabled = ko.pureComputed({ + read: function () { + return _this.getSelectedRoles().length > 0; + }, + deferEvaluation: true + }); + return _this; + } + RexDeleteRoleDialog.prototype.onConfirm = function () { + var _ = wsAmeLodash; + var rolesToDelete = this.getSelectedRoles(); + //Warn about the dangers of deleting built-in roles. + var selectedBuiltInRoles = _.filter(rolesToDelete, _.method('isBuiltIn')); + if (selectedBuiltInRoles.length > 0) { + var warning = 'Caution: Deleting default roles like ' + _.first(selectedBuiltInRoles).displayName() + + ' can prevent you from using certain plugins. This is because some plugins look for specific' + + ' role names to determine if a user is allowed to access the plugin.' + + '\nDelete ' + selectedBuiltInRoles.length + ' default role(s)?'; + if (!confirm(warning)) { + return; + } + } + this.editor.deleteRoles(rolesToDelete); + this.isOpen(false); + }; + RexDeleteRoleDialog.prototype.onOpen = function (event, ui) { + //Deselect all previously selected roles. + wsAmeLodash.forEach(this.isRoleSelected, function (isSelected) { + isSelected(false); + }); + }; + RexDeleteRoleDialog.prototype.getSelectionState = function (roleName) { + if (!this.isRoleSelected.hasOwnProperty(roleName)) { + this.isRoleSelected[roleName] = ko.observable(false); + } + return this.isRoleSelected[roleName]; + }; + RexDeleteRoleDialog.prototype.getSelectedRoles = function () { + var _this = this; + var _ = wsAmeLodash; + var rolesToDelete = []; + _.forEach(this.editor.roles(), function (role) { + if (_this.getSelectionState(role.name())()) { + rolesToDelete.push(role); + } + }); + return rolesToDelete; + }; + return RexDeleteRoleDialog; +}(RexBaseDialog)); +var RexRenameRoleDialog = /** @class */ (function (_super) { + __extends(RexRenameRoleDialog, _super); + function RexRenameRoleDialog(editor) { + var _this = _super.call(this) || this; + _this.selectedRole = ko.observable(null); + _this.newDisplayName = ko.observable(''); + _this.displayNameValidationMessage = ko.observable(''); + _this.isTooltipInitialised = false; + _this.editor = editor; + _this.options.minWidth = 380; + _this.options.buttons.push({ + text: 'Rename Role', + 'class': 'button button-primary', + click: _this.onConfirm.bind(_this), + disabled: true + }); + _this.selectedRole.subscribe(function (role) { + if (role) { + _this.newDisplayName(role.displayName()); + } + }); + _this.isConfirmButtonEnabled = ko.computed({ + read: function () { + return RexAddRoleDialog.validateDisplayName(_this.newDisplayName(), _this.displayNameValidationMessage); + }, + deferEvaluation: true + }); + return _this; + } + RexRenameRoleDialog.prototype.onOpen = function (event, ui) { + var _ = wsAmeLodash; + if (!this.isTooltipInitialised) { + this.setupValidationTooltip('#rex-edited-role-display-name', this.displayNameValidationMessage); + this.isTooltipInitialised = true; + } + //Select either the currently selected role or the first available role. + var selectedActor = this.editor.selectedActor(); + if (selectedActor && (selectedActor instanceof RexRole)) { + this.selectedRole(selectedActor); + } + else { + this.selectedRole(_.first(this.editor.roles())); + } + }; + RexRenameRoleDialog.prototype.onConfirm = function () { + if (!this.isConfirmButtonEnabled()) { + return; + } + if (this.selectedRole()) { + var name_1 = this.newDisplayName().trim(); + this.selectedRole().displayName(name_1); + this.editor.actorSelector.repopulate(); + } + this.isOpen(false); + }; + return RexRenameRoleDialog; +}(RexBaseDialog)); +var RexEagerObservableStringSet = /** @class */ (function () { + function RexEagerObservableStringSet() { + this.items = {}; + } + RexEagerObservableStringSet.prototype.contains = function (item) { + if (!this.items.hasOwnProperty(item)) { + this.items[item] = ko.observable(false); + return false; + } + return this.items[item](); + }; + RexEagerObservableStringSet.prototype.add = function (item) { + if (!this.items.hasOwnProperty(item)) { + this.items[item] = ko.observable(true); + } + else { + this.items[item](true); + } + }; + RexEagerObservableStringSet.prototype.remove = function (item) { + if (this.items.hasOwnProperty(item)) { + this.items[item](false); + } + }; + RexEagerObservableStringSet.prototype.clear = function () { + var _ = wsAmeLodash; + _.forEach(this.items, function (isInSet) { + isInSet(false); + }); + }; + RexEagerObservableStringSet.prototype.getPresenceObservable = function (item) { + if (!this.items.hasOwnProperty(item)) { + this.items[item] = ko.observable(false); + } + return this.items[item]; + }; + RexEagerObservableStringSet.prototype.getAsObject = function (fillValue) { + if (fillValue === void 0) { fillValue = true; } + var _ = wsAmeLodash; + var output = {}; + _.forEach(this.items, function (isInSet, item) { + if (isInSet()) { + output[item] = fillValue; + } + }); + return output; + }; + return RexEagerObservableStringSet; +}()); +var RexObservableEditableRoleSettings = /** @class */ (function () { + function RexObservableEditableRoleSettings() { + this.strategy = ko.observable('auto'); + this.userDefinedList = new RexEagerObservableStringSet(); + } + RexObservableEditableRoleSettings.prototype.toPlainObject = function () { + var roleList = this.userDefinedList.getAsObject(true); + if (wsAmeLodash.isEmpty(roleList)) { + roleList = null; + } + return { + strategy: this.strategy(), + userDefinedList: roleList + }; + }; + return RexObservableEditableRoleSettings; +}()); +var RexUserRoleModule = /** @class */ (function () { + function RexUserRoleModule(selectedActor, roles) { + var _this = this; + this.roleObservables = {}; + this.selectedActor = selectedActor; + this.sortedRoles = ko.computed(function () { + return roles(); + }); + this.primaryRole = ko.computed({ + read: function () { + var actor = selectedActor(); + if ((actor === null) || !actor.canHaveRoles) { + return null; + } + if (actor instanceof RexUser) { + var roles_1 = actor.roles(); + if (roles_1.length < 1) { + return null; + } + return roles_1[0]; + } + return null; + }, + write: function (newRole) { + var actor = selectedActor(); + if ((actor === null) || !actor.canHaveRoles || !(actor instanceof RexUser)) { + return; + } + //No primary role = no roles at all. + if (newRole === null) { + actor.roles.removeAll(); + return; + } + //Sanity check. + if (!(newRole instanceof RexRole)) { + return; + } + if (!_this.canAssignRoleToActor(newRole)) { + return; + } + //Remove the previous primary role. + var oldPrimaryRole = (actor.roles().length > 0) ? actor.roles()[0] : null; + if (oldPrimaryRole !== null) { + actor.roles.remove(oldPrimaryRole); + } + //If the user already has the new role, remove it from its old position first. + if (actor.roles.indexOf(newRole) !== -1) { + actor.roles.remove(newRole); + } + //Add the role to the top of the list. + actor.roles.unshift(newRole); + } + }); + this.isVisible = ko.pureComputed(function () { + var actor = _this.selectedActor(); + return (actor !== null) && actor.canHaveRoles; + }); + } + // noinspection JSUnusedGlobalSymbols Used in Knockout templates. + RexUserRoleModule.prototype.actorHasRole = function (role) { + var _this = this; + var roleActorId = role.getId(); + if (this.roleObservables.hasOwnProperty(roleActorId) && (this.roleObservables[roleActorId].role === role)) { + return this.roleObservables[roleActorId].selectedActorHasRole; + } + var selectedActorHasRole = ko.computed({ + read: function () { + var actor = _this.selectedActor(); + if ((actor === null) || !actor.canHaveRoles) { + return false; + } + if (actor instanceof RexUser) { + return (actor.roles.indexOf(role) !== -1); + } + return false; + }, + write: function (shouldHaveRole) { + var actor = _this.selectedActor(); + if ((actor === null) || !actor.canHaveRoles || !(actor instanceof RexUser)) { + return; + } + if (!_this.canAssignRoleToActor(role)) { + return; + } + var alreadyHasRole = (actor.roles.indexOf(role) !== -1); + if (shouldHaveRole !== alreadyHasRole) { + if (shouldHaveRole) { + actor.roles.push(role); + } + else { + actor.roles.remove(role); + } + } + } + }); + this.roleObservables[roleActorId] = { + role: role, + selectedActorHasRole: selectedActorHasRole + }; + return selectedActorHasRole; + }; + RexUserRoleModule.prototype.canAssignRoleToActor = function (role) { + //This is a stub. The role editor currently doesn't check editable role settings at edit time. + var actor = this.selectedActor(); + if ((actor === null) || !actor.canHaveRoles) { + return false; + } + return (role instanceof RexRole); + }; + return RexUserRoleModule; +}()); +var RexEditableRolesDialog = /** @class */ (function (_super) { + __extends(RexEditableRolesDialog, _super); + function RexEditableRolesDialog(editor) { + var _this = _super.call(this) || this; + _this.selectedActor = ko.observable(null); + _this.actorSettings = {}; + _this.editor = editor; + _this.visibleActors = ko.observableArray([]); + _this.options.minWidth = 600; + _this.options.buttons.push({ + text: 'Save Changes', + 'class': 'button button-primary', + click: _this.onConfirm.bind(_this), + disabled: false + }); + //Super Admin is always set to "leave unchanged" because + //they can edit all roles. + var superAdmin = editor.getSuperAdmin(); + var superAdminSettings = new RexObservableEditableRoleSettings(); + superAdminSettings.strategy('none'); + var dummySettings = new RexObservableEditableRoleSettings(); + _this.selectedActorSettings = ko.computed(function () { + if (_this.selectedActor() === null) { + return dummySettings; + } + if (_this.selectedActor() === superAdmin) { + return superAdminSettings; + } + var actorId = _this.selectedActor().getId(); + if (!_this.actorSettings.hasOwnProperty(actorId)) { + //This should never happen; the dictionary should be initialised when opening the dialog. + _this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); + } + return _this.actorSettings[actorId]; + }); + _this.editableRoleStrategy = ko.computed({ + read: function () { + return _this.selectedActorSettings().strategy(); + }, + write: function (newValue) { + _this.selectedActorSettings().strategy(newValue); + } + }); + _this.isAutoStrategyAllowed = ko.computed(function () { + var actor = _this.selectedActor(); + if (actor == null) { + return true; + } + return !((actor === superAdmin) + || ((actor instanceof RexUser) && actor.isSuperAdmin)); + }); + _this.isListStrategyAllowed = _this.isAutoStrategyAllowed; + return _this; + } + RexEditableRolesDialog.prototype.onOpen = function (event, ui) { + var _this = this; + var _ = wsAmeLodash; + //Copy editable role settings into observables. + _.forEach(this.editor.actorEditableRoles, function (settings, actorId) { + if (!_this.actorSettings.hasOwnProperty(actorId)) { + _this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); + } + var observableSettings = _this.actorSettings[actorId]; + observableSettings.strategy(settings.strategy); + observableSettings.userDefinedList.clear(); + if (settings.userDefinedList !== null) { + _.forEach(settings.userDefinedList, function (ignored, roleId) { + observableSettings.userDefinedList.add(roleId); + }); + } + }); + this.visibleActors(this.editor.actorSelector.getVisibleActors()); + //Select either the currently selected actor or the first role. + var selectedActor = this.editor.selectedActor(); + if (selectedActor) { + this.selectedActor(selectedActor); + } + else { + this.selectedActor(_.first(this.editor.roles())); + } + }; + RexEditableRolesDialog.prototype.onConfirm = function () { + //Save editable roles + var _ = wsAmeLodash; + var settings = this.editor.actorEditableRoles; + _.forEach(this.actorSettings, function (observableSettings, actorId) { + if (observableSettings.strategy() === 'auto') { + //"auto" is the default so we don't need to store anything. + delete settings[actorId]; + } + else { + settings[actorId] = observableSettings.toPlainObject(); + } + }); + this.isOpen(false); + }; + RexEditableRolesDialog.prototype.isRoleSetToEditable = function (role) { + return this.selectedActorSettings().userDefinedList.getPresenceObservable(role.name()); + }; + RexEditableRolesDialog.prototype.isRoleEnabled = function (role) { + return this.editableRoleStrategy() === 'user-defined-list'; + }; + RexEditableRolesDialog.prototype.selectItem = function (actor) { + this.selectedActor(actor); + }; + RexEditableRolesDialog.prototype.getItemText = function (actor) { + return this.editor.actorSelector.getNiceName(actor); + }; + return RexEditableRolesDialog; +}(RexBaseDialog)); +var RexRoleEditor = /** @class */ (function () { + function RexRoleEditor(data) { + var _this = this; + // noinspection JSUnusedGlobalSymbols + this.categoryViewOptions = [ + RexRoleEditor.hierarchyView, + RexRoleEditor.singleCategoryView, + RexRoleEditor.listView + ]; + this.deprecatedCapabilities = {}; + this.userDefinedCapabilities = {}; + this.categoriesBySlug = {}; + this.actorLookup = {}; + var self = this; + var _ = wsAmeLodash; + this.areBindingsApplied = ko.observable(false); + this.isLoaded = ko.computed(function () { + return _this.areBindingsApplied(); + }); + this.userPreferences = new RexUserPreferences(data.userPreferences, data.adminAjaxUrl, data.updatePreferencesNonce); + var preferences = this.userPreferences; + this.showDeprecatedEnabled = preferences.getObservable('showDeprecatedEnabled', true); + this.showRedundantEnabled = preferences.getObservable('showRedundantEnabled', false); + this.showBaseCapsEnabled = ko.computed(this.showRedundantEnabled); + this.showOnlyCheckedEnabled = preferences.getObservable('showOnlyCheckedEnabled', false); + this.categoryWidthMode = preferences.getObservable('categoryWidthMode', 'adaptive'); + this.readableNamesEnabled = preferences.getObservable('readableNamesEnabled', true); + this.showNumberOfCapsEnabled = preferences.getObservable('showNumberOfCapsEnabled', true); + this.showGrantedCapCountEnabled = preferences.getObservable('showGrantedCapCountEnabled', true); + this.showTotalCapCountEnabled = preferences.getObservable('showTotalCapCountEnabled', true); + this.showZerosEnabled = preferences.getObservable('showZerosEnabled', false); + this.inheritanceOverrideEnabled = preferences.getObservable('inheritanceOverrideEnabled', false); + //Remember and restore the selected view mode. + var viewModeId = preferences.getObservable('categoryVewMode', 'hierarchy'); + var initialViewMode = _.find(this.categoryViewOptions, 'id', viewModeId()); + if (!initialViewMode) { + initialViewMode = RexRoleEditor.hierarchyView; + } + this.categoryViewMode = ko.observable(initialViewMode); + this.categoryViewMode.subscribe(function (newMode) { + viewModeId(newMode.id); + }); + this.isShiftKeyDown = ko.observable(false); + this.capabilityViewClasses = ko.pureComputed({ + read: function () { + var viewMode = _this.categoryViewMode(); + var classes = ['rex-category-view-mode-' + viewMode.id]; + if (viewMode === RexRoleEditor.singleCategoryView) { + classes.push('rex-show-category-subheadings'); + } + if (_this.readableNamesEnabled()) { + classes.push('rex-readable-names-enabled'); + } + if (_this.categoryWidthMode() === 'full') { + classes.push('rex-full-width-categories'); + } + return classes.join(' '); + }, + deferEvaluation: true + }); + this.searchQuery = ko.observable('').extend({ rateLimit: { timeout: 100, method: "notifyWhenChangesStop" } }); + this.searchKeywords = ko.computed(function () { + var query = self.searchQuery().trim(); + if (query === '') { + return []; + } + return wsAmeLodash(query.split(' ')) + .map(function (keyword) { + return keyword.trim(); + }) + .filter(function (keyword) { + return (keyword !== ''); + }) + .value(); + }); + this.components = _.mapValues(data.knownComponents, function (details, id) { + return RexWordPressComponent.fromJs(id, details); + }); + this.coreComponent = new RexWordPressComponent(':wordpress:', 'WordPress core'); + this.components[':wordpress:'] = this.coreComponent; + //Populate roles and users. + var tempRoleList = []; + _.forEach(data.roles, function (roleData) { + var role = new RexRole(roleData.name, roleData.displayName, roleData.capabilities); + role.hasUsers = roleData.hasUsers; + tempRoleList.push(role); + _this.actorLookup[role.id()] = role; + }); + this.roles = ko.observableArray(tempRoleList); + var tempUserList = []; + _.forEach(AmeActors.getUsers(), function (data) { + var user = RexUser.fromAmeUser(data, self); + tempUserList.push(user); + _this.actorLookup[user.id()] = user; + }); + this.users = ko.observableArray(tempUserList); + this.dummyActor = new RexRole('rex-invalid-role', 'Invalid Role'); + this.defaultNewUserRoleName = data.defaultRoleName; + this.trashedRoles = ko.observableArray(_.map(data.trashedRoles, function (roleData) { + return RexRole.fromRoleData(roleData); + })); + this.actorSelector = new AmeActorSelector(this, true, false); + //Wrap the selected actor in a computed observable so that it can be used with Knockout. + var _selectedActor = ko.observable(this.getActor(this.actorSelector.selectedActor)); + this.selectedActor = ko.computed({ + read: function () { + return _selectedActor(); + }, + write: function (newActor) { + _this.actorSelector.setSelectedActor(newActor.id()); + } + }); + this.actorSelector.onChange(function (newSelectedActor) { + _selectedActor(_this.getActor(newSelectedActor)); + }); + //Refresh the actor selector when roles are added or removed. + this.roles.subscribe(function () { + _this.actorSelector.repopulate(); + }); + //Re-select the previously selected actor if possible. + var initialActor = null; + if (data.selectedActor) { + initialActor = this.getActor(data.selectedActor); + } + if (!initialActor || (initialActor === this.dummyActor)) { + initialActor = this.roles()[0]; + } + this.selectedActor(initialActor); + //Populate capabilities. + this.deprecatedCapabilities = data.deprecatedCapabilities; + this.metaCapabilityMap = data.metaCapMap; + this.userDefinedCapabilities = data.userDefinedCapabilities; + this.capabilities = _.mapValues(data.capabilities, function (metadata, name) { + return RexCapability.fromJs(name, metadata, self); + }); + //Add the special "do_not_allow" capability. Normally, it's impossible to assign it to anyone, + //but it can still be used in post type permissions and other places. + var doNotAllow = new RexDoNotAllowCapability(this); + doNotAllow.originComponent = this.components[':wordpress:']; + this.capabilities['do_not_allow'] = doNotAllow; + //Similarly, "exist" is always enabled for all roles and users. Everyone can exist. + if (this.capabilities.hasOwnProperty('exist')) { + this.capabilities['exist'] = new RexExistCapability(this); + this.capabilities['exist'].originComponent = this.components[':wordpress:']; + } + //Store editable roles. + this.actorEditableRoles = (!_.isEmpty(data.editableRoles)) ? data.editableRoles : {}; + this.rootCategory = new RexCategory('All', this); + var coreCategory = RexCategory.fromJs(data.coreCategory, this); + this.rootCategory.addSubcategory(coreCategory); + var postTypeCategory = new RexPostTypeContainerCategory('Post Types', this, 'postTypes'); + this.postTypes = _.indexBy(data.postTypes, 'name'); + _.forEach(this.postTypes, function (details, id) { + var category = new RexPostTypeCategory(details.label, self, id, 'postTypes/' + id, details.permissions, details.isDefault); + if (details.componentId) { + category.origin = _this.getComponent(details.componentId); + } + postTypeCategory.addSubcategory(category); + //Record the post type actions associated with each capability. + for (var action in details.permissions) { + var capability = self.getCapability(details.permissions[action]); + _.set(capability.usedByPostTypeActions, [details.name, action], true); + } + }); + //Sort the actual subcategory array. + postTypeCategory.sortSubcategories(); + this.rootCategory.addSubcategory(postTypeCategory); + //Taxonomies. + this.taxonomies = data.taxonomies; + var taxonomyCategory = new RexTaxonomyContainerCategory('Taxonomies', this, 'taxonomies'); + _.forEach(data.taxonomies, function (details, id) { + var category = new RexTaxonomyCategory(details.label, self, id, 'taxonomies/' + id, details.permissions); + taxonomyCategory.addSubcategory(category); + //Record taxonomy type actions associated with each capability. + for (var action in details.permissions) { + var capability = self.getCapability(details.permissions[action]); + _.set(capability.usedByTaxonomyActions, [details.name, action], true); + } + }); + taxonomyCategory.subcategories.sort(function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + this.rootCategory.addSubcategory(taxonomyCategory); + var customParentCategory = new RexCategory('Plugins', this, 'custom'); + function initCustomCategory(details, parent) { + var category = RexCategory.fromJs(details, self); + //Sort subcategories by title. + category.subcategories.sort(function (a, b) { + //Keep the "General" category at the top if there is one. + if (a.name === b.name) { + return 0; + } + else if (a.name === 'General') { + return -1; + } + else if (b.name === 'General') { + return 1; + } + return a.name.localeCompare(b.name); + }); + parent.addSubcategory(category); + } + _.forEach(data.customCategories, function (details) { + initCustomCategory(details, customParentCategory); + }); + customParentCategory.subcategories.sort(function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + this.rootCategory.addSubcategory(customParentCategory); + //Make a category for uncategorized capabilities. This one is always at the bottom. + var uncategorizedCategory = new RexCategory('Uncategorized', self, 'custom/uncategorized', data.uncategorizedCapabilities); + customParentCategory.addSubcategory(uncategorizedCategory); + var _selectedCategory = ko.observable(null); + this.selectedCategory = ko.computed({ + read: function () { + return _selectedCategory(); + }, + write: function (newSelection) { + var oldSelection = _selectedCategory(); + if (newSelection === oldSelection) { + return; + } + if (newSelection) { + newSelection.isSelected(true); + } + if (oldSelection) { + oldSelection.isSelected(false); + } + _selectedCategory(newSelection); + } + }); + this.selectedCategory(this.rootCategory); + this.permissionTipSubject = ko.observable(null); + this.allCapabilitiesAsPermissions = ko.pureComputed({ + read: function () { + //Create a permission for each unique, non-deleted capability. + //Exclude special caps like do_not_allow and exist because they can't be enabled. + var excludedCaps = ['do_not_allow', 'exist']; + return _.chain(_this.capabilities) + .map(function (capability) { + if (excludedCaps.indexOf(capability.name) >= 0) { + return null; + } + return new RexPermission(self, capability); + }) + .filter(function (value) { + return value !== null; + }) + .value(); + }, + deferEvaluation: true + }); + this.capsInSelectedCategory = ko.pureComputed({ + read: function () { + var category = _this.selectedCategory(); + if (!category) { + return {}; + } + var caps = {}; + category.countUniqueCapabilities(caps); + return caps; + }, + deferEvaluation: true + }); + this.leafCategories = ko.computed({ + read: function () { + //So what we want here is a depth-first traversal of the category tree. + var results = []; + var addedUniqueCategories = {}; + function traverse(category) { + if (category.subcategories.length < 1) { + //Eliminate duplicates, like CPTs that show up in the post type category and a plugin category. + var key = category.getDeDuplicationKey(); + if (!addedUniqueCategories.hasOwnProperty(key)) { + results.push(category); + addedUniqueCategories[key] = category; + } + else { + addedUniqueCategories[key].addDuplicate(category); + } + return; + } + for (var i = 0; i < category.subcategories.length; i++) { + traverse(category.subcategories[i]); + } + } + traverse(_this.rootCategory); + results.sort(function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + return results; + }, + deferEvaluation: true + }); + var compareRoleDisplayNames = function (a, b) { + return a.displayName().toLowerCase().localeCompare(b.displayName().toLowerCase()); + }; + this.defaultRoles = ko.pureComputed({ + read: function () { + return _.filter(self.roles(), function (role) { + return role.isBuiltIn(); + }).sort(compareRoleDisplayNames); + }, + deferEvaluation: true + }); + this.customRoles = ko.computed({ + read: function () { + return _.difference(self.roles(), self.defaultRoles()).sort(compareRoleDisplayNames); + }, + deferEvaluation: true + }); + this.deleteCapabilityDialog = new RexDeleteCapDialog(this); + this.addCapabilityDialog = new RexAddCapabilityDialog(this); + this.addRoleDialog = new RexAddRoleDialog(this); + this.deleteRoleDialog = new RexDeleteRoleDialog(this); + this.renameRoleDialog = new RexRenameRoleDialog(this); + this.editableRolesDialog = new RexEditableRolesDialog(this); + this.userRoleModule = new RexUserRoleModule(this.selectedActor, this.roles); + this.settingsFieldData = ko.observable(''); + this.isSaving = ko.observable(false); + this.isGlobalSettingsUpdate = ko.observable(false); + } + RexRoleEditor.prototype.capabilityMatchesFilters = function (capability) { + if (!this.showDeprecatedEnabled() && this.isDeprecated(capability.name)) { + return false; + } + if (this.showOnlyCheckedEnabled() && !capability.isEnabledForSelectedActor()) { + return false; + } + var keywords = this.searchKeywords(), capabilityName = capability.name; + if (keywords.length > 0) { + var haystack_1 = capabilityName.toLowerCase(); + var matchesKeywords = wsAmeLodash.all(keywords, function (keyword) { + return haystack_1.indexOf(keyword) >= 0; + }); + if (!matchesKeywords) { + return false; + } + } + return true; + }; + RexRoleEditor.prototype.isDeprecated = function (capability) { + return this.deprecatedCapabilities.hasOwnProperty(capability); + }; + RexRoleEditor.prototype.getComponent = function (componentId) { + if (this.components.hasOwnProperty(componentId)) { + return this.components[componentId]; + } + return null; + }; + /** + * Get or create a capability instance. + */ + RexRoleEditor.prototype.getCapability = function (capabilityName, recursionDepth) { + if (recursionDepth === void 0) { recursionDepth = 0; } + //Un-map meta capabilities where possible. + if (this.metaCapabilityMap.hasOwnProperty(capabilityName) && (recursionDepth < 10)) { + return this.getCapability(this.metaCapabilityMap[capabilityName], recursionDepth + 1); + } + if (!this.capabilities.hasOwnProperty(capabilityName)) { + var _1 = wsAmeLodash; + if (!_1.isString(capabilityName) && !_1.isFinite(capabilityName)) { + return this.getInvalidCapability(capabilityName); + } + if (console && console.info) { + console.info('Capability not found: "' + capabilityName + '". It will be created.'); + } + capabilityName = String(capabilityName); + this.capabilities[capabilityName] = new RexCapability(capabilityName, this); + } + return this.capabilities[capabilityName]; + }; + RexRoleEditor.prototype.getInvalidCapability = function (invalidName) { + var capabilityName = '[Invalid capability: ' + String(invalidName) + ']'; + if (!this.capabilities.hasOwnProperty(capabilityName)) { + if (console && console.error) { + console.error('Invalid capability detected - expected a string but got this: ', invalidName); + } + this.capabilities[capabilityName] = new RexInvalidCapability(capabilityName, invalidName, this); + } + return this.capabilities[capabilityName]; + }; + RexRoleEditor.prototype.getActor = function (actorId) { + if (this.actorLookup.hasOwnProperty(actorId)) { + return this.actorLookup[actorId]; + } + return this.dummyActor; + }; + RexRoleEditor.prototype.getRole = function (name) { + var actorId = 'role:' + name; + if (this.actorLookup.hasOwnProperty(actorId)) { + var role = this.actorLookup[actorId]; + if (role instanceof RexRole) { + return role; + } + } + return null; + }; + // noinspection JSUnusedGlobalSymbols Testing method used in KO templates. + RexRoleEditor.prototype.setSubjectPermission = function (permission) { + this.permissionTipSubject(permission); + }; + /** + * Search a string for the current search keywords and add the "rex-search-highlight" CSS class to each match. + * + * @param inputString + */ + RexRoleEditor.prototype.highlightSearchKeywords = function (inputString) { + var _ = wsAmeLodash; + var keywordList = this.searchKeywords(); + if (keywordList.length === 0) { + return inputString; + } + var keywordGroup = _.map(keywordList, _.escapeRegExp).join('|'); + var regex = new RegExp('((?:' + keywordGroup + ')(?:\\s*))+', 'gi'); + return inputString.replace(regex, function (foundKeywords) { + //Don't highlight the trailing space after the keyword(s). + var trailingSpace = ''; + var parts = foundKeywords.match(/^(.+?)(\s+)$/); + if (parts) { + foundKeywords = parts[1]; + trailingSpace = parts[2]; + } + return '' + foundKeywords + '' + trailingSpace; + }); + }; + RexRoleEditor.prototype.actorExists = function (actorId) { + return this.actorLookup.hasOwnProperty(actorId); + }; + RexRoleEditor.prototype.addUsers = function (newUsers) { + var _this = this; + wsAmeLodash.forEach(newUsers, function (user) { + if (!(user instanceof RexUser)) { + if (console.error) { + console.error('Cannot add a user. Expected an instance of RexUser, got this:', user); + } + return; + } + if (!_this.actorLookup.hasOwnProperty(user.getId())) { + _this.users.push(user); + _this.actorLookup[user.getId()] = user; + } + }); + }; + RexRoleEditor.prototype.createUserFromProperties = function (properties) { + return RexUser.fromAmeUserProperties(properties, this); + }; + RexRoleEditor.prototype.getRoles = function () { + return wsAmeLodash.indexBy(this.roles(), function (role) { + return role.name(); + }); + }; + RexRoleEditor.prototype.getSuperAdmin = function () { + return RexSuperAdmin.getInstance(); + }; + RexRoleEditor.prototype.getUser = function (login) { + var actorId = 'user:' + login; + if (this.actorLookup.hasOwnProperty(actorId)) { + var user = this.actorLookup[actorId]; + if (user instanceof RexUser) { + return user; + } + } + return null; + }; + RexRoleEditor.prototype.getUsers = function () { + return wsAmeLodash.indexBy(this.users(), 'userLogin'); + }; + RexRoleEditor.prototype.isInSelectedCategory = function (capabilityName) { + var caps = this.capsInSelectedCategory(); + return caps.hasOwnProperty(capabilityName); + }; + RexRoleEditor.prototype.addCapability = function (capabilityName) { + var capability; + if (this.capabilities.hasOwnProperty(capabilityName)) { + capability = this.capabilities[capabilityName]; + if (!capability.isDeleted()) { + throw 'Cannot add capability "' + capabilityName + '" because it already exists.'; + } + capability.isDeleted(false); + this.userDefinedCapabilities[capabilityName] = true; + return null; + } + else { + capability = new RexCapability(capabilityName, this); + capability.notes = 'This capability has not been saved yet. Click the "Save Changes" button to save it.'; + this.capabilities[capabilityName] = capability; + //Add the new capability to the "Other" or "Uncategorized" category. + var category = this.categoriesBySlug['custom/uncategorized']; + var permission = new RexPermission(this, capability); + category.permissions.push(permission); + category.sortPermissions(); + this.userDefinedCapabilities[capabilityName] = true; + return category; + } + }; + RexRoleEditor.prototype.deleteCapabilities = function (selectedCapabilities) { + var self = this, _ = wsAmeLodash; + var targetActors = _.union(this.roles(), this.users()); + _.forEach(selectedCapabilities, function (capability) { + //Remove it from all roles and visible users. + _.forEach(targetActors, function (actor) { + actor.deleteCap(capability.name); + }); + capability.isDeleted(true); + delete self.userDefinedCapabilities[capability.name]; + }); + }; + RexRoleEditor.prototype.capabilityExists = function (capabilityName) { + return this.capabilities.hasOwnProperty(capabilityName) && !this.capabilities[capabilityName].isDeleted(); + }; + RexRoleEditor.prototype.addRole = function (name, displayName, capabilities) { + if (capabilities === void 0) { capabilities = {}; } + var role = new RexRole(name, displayName, capabilities); + this.actorLookup[role.id()] = role; + this.roles.push(role); + //Select the new role. + this.selectedActor(role); + return role; + }; + RexRoleEditor.prototype.deleteRoles = function (roles) { + var _this = this; + var _ = wsAmeLodash; + _.forEach(roles, function (role) { + if (!_this.canDeleteRole(role)) { + throw 'Cannot delete role "' + role.name() + '"'; + } + }); + this.roles.removeAll(roles); + this.trashedRoles.push.apply(this.trashedRoles, roles); + //TODO: Later, add an option to restore deleted roles. + }; + RexRoleEditor.prototype.canDeleteRole = function (role) { + //Was the role already assigned to any users when the editor was opened? + if (role.hasUsers) { + return false; + } + //We also need to take into account any unsaved user role changes. + //Is the role assigned to any of the users currently loaded in the editor? + var _ = wsAmeLodash; + if (_.some(this.users(), function (user) { + return (user.roles.indexOf(role) !== -1); + })) { + return false; + } + return !this.isDefaultRoleForNewUsers(role); + }; + RexRoleEditor.prototype.isDefaultRoleForNewUsers = function (role) { + return (role.name() === this.defaultNewUserRoleName); + }; + // noinspection JSUnusedGlobalSymbols Used in KO templates. + RexRoleEditor.prototype.saveChanges = function () { + this.isSaving(true); + var _ = wsAmeLodash; + var data = { + 'roles': _.invoke(this.roles(), 'toJs'), + 'users': _.invoke(this.users(), 'toJs'), + 'trashedRoles': _.invoke(this.trashedRoles(), 'toJs'), + 'userDefinedCaps': _.keys(this.userDefinedCapabilities), + 'editableRoles': this.actorEditableRoles + }; + this.settingsFieldData(ko.toJSON(data)); + jQuery('#rex-save-settings-form').submit(); + }; + RexRoleEditor.prototype.updateAllSites = function () { + if (!confirm('Apply these role settings to ALL sites? Any changes that you\'ve made to individual sites will be lost.')) { + return false; + } + this.isGlobalSettingsUpdate(true); + this.saveChanges(); + }; + RexRoleEditor.hierarchyView = { + label: 'Hierarchy view', + id: 'hierarchy', + templateName: 'rex-hierarchy-view-template' + }; + RexRoleEditor.singleCategoryView = { + label: 'Category view', + id: 'category', + templateName: 'rex-single-category-view-template' + }; + RexRoleEditor.listView = { label: 'List view', id: 'list', templateName: 'rex-list-view-template' }; + return RexRoleEditor; +}()); +(function () { + jQuery(function ($) { + var rootElement = jQuery('#ame-role-editor-root'); + //Initialize the application. + var app = new RexRoleEditor(wsRexRoleEditorData); + //The input data can be quite large, so let's give the browser a chance to free up that memory. + wsRexRoleEditorData = null; + window['ameRoleEditor'] = app; + //console.time('Apply Knockout bindings'); + //ko.options.deferUpdates = true; + ko.applyBindings(app, rootElement.get(0)); + app.areBindingsApplied(true); + //console.timeEnd('Apply Knockout bindings'); + //Track the state of the Shift key. + var isShiftKeyDown = false; + function handleKeyboardEvent(event) { + var newState = !!(event.shiftKey); + if (newState !== isShiftKeyDown) { + isShiftKeyDown = newState; + app.isShiftKeyDown(isShiftKeyDown); + } + } + $(document).on('keydown.adminMenuEditorRex keyup.adminMenuEditorRex mousedown.adminMenuEditorRex', handleKeyboardEvent); + //Initialize permission tooltips. + var visiblePermissionTooltips = []; + rootElement.find('#rex-capability-view').on('mouseenter click', '.rex-permission-tip-trigger', function (event) { + $(this).qtip({ + overwrite: false, + content: { + text: 'Loading...' + }, + //Show the tooltip on focus. + show: { + event: 'click mouseenter', + delay: 80, + solo: '#ame-role-editor-root', + ready: true, + effect: false + }, + hide: { + event: 'mouseleave unfocus', + fixed: true, + delay: 300, + leave: false, + effect: false + }, + position: { + my: 'center left', + at: 'center right', + effect: false, + viewport: $(window), + adjust: { + method: 'flipinvert shift', + scroll: false, + } + }, + style: { + classes: 'qtip-bootstrap qtip-shadow rex-tooltip' + }, + events: { + show: function (event, api) { + //Immediately hide all other permission tooltips. + for (var i = visiblePermissionTooltips.length - 1; i >= 0; i--) { + visiblePermissionTooltips[i].hide(); + } + var permission = ko.dataFor(api.target.get(0)); + if (permission && (permission instanceof RexPermission)) { + app.permissionTipSubject(permission); + } + //Move the content container to the current tooltip. + var tipContent = $('#rex-permission-tip'); + if (!$.contains(api.elements.content.get(0), tipContent.get(0))) { + api.elements.content.empty().append(tipContent); + } + visiblePermissionTooltips.push(api); + }, + hide: function (event, api) { + var index = visiblePermissionTooltips.indexOf(api); + if (index >= 0) { + visiblePermissionTooltips.splice(index, 1); + } + } + } + }, event); + }); + //Tooltips must have a higher z-index than the modal widget overlay and the Toolbar. + jQuery.fn.qtip.zindex = 100101 + 5000; + //Set up dropdown menus. + $('.rex-dropdown-trigger').on('click', function (event) { + var $trigger = $(this); + var $dropdown = $('#' + $trigger.data('target-dropdown-id')); + event.stopPropagation(); + event.preventDefault(); + function hideThisDropdown(event) { + //Only do it if the user clicked something outside the dropdown. + var $clickedDropdown = $(event.target).closest($dropdown.get(0)); + if ($clickedDropdown.length < 1) { + $dropdown.hide(); + $(document).off('click', hideThisDropdown); + } + } + if ($dropdown.is(':visible')) { + $dropdown.hide(); + $(document).off('click', hideThisDropdown); + return; + } + $dropdown.show().position({ + my: 'left top', + at: 'left bottom', + of: $trigger + }); + $(document).on('click', hideThisDropdown); + }); + }); +})(); +//# sourceMappingURL=role-editor.js.map \ No newline at end of file diff --git a/extras/modules/role-editor/role-editor.js.map b/extras/modules/role-editor/role-editor.js.map new file mode 100644 index 0000000..17de9a2 --- /dev/null +++ b/extras/modules/role-editor/role-editor.js.map @@ -0,0 +1 @@ +{"version":3,"file":"role-editor.js","sourceRoot":"","sources":["role-editor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/role-editor/role-editor.scss b/extras/modules/role-editor/role-editor.scss new file mode 100644 index 0000000..fdc277b --- /dev/null +++ b/extras/modules/role-editor/role-editor.scss @@ -0,0 +1,955 @@ +@import "../../../css/boxes"; + +$boxBorder: 1px solid $amePostboxBorderColor; +$boxTopPadding: 10px; +$boxLeftPadding: 8px; +$boxPadding: $boxTopPadding $boxLeftPadding; + +$capViewContainerPaddingLeft: $boxTopPadding; +$capViewContainerPaddingTop: $boxTopPadding; + +$onePermissionHeight: 27px; + +@mixin ame-postbox { + border: $boxBorder; + background: #fff; + box-shadow: $amePostboxShadow; +} + +@mixin ame-striped-table { + tbody tr:nth-child(2n+1) { + background-color: #F9F9F9; + } +} + +#rex-loading-message { + margin-top: 10px; +} + +#rex-main-ui { + display: flex; + flex-direction: row; + margin-top: 10px; + width: 100%; +} + +#rex-content-container, +#rex-action-sidebar { + @include ame-postbox; +} + +#rex-content-container { + display: flex; + flex-grow: 80; + padding: 0; + overflow-x: hidden; +} + +#rex-action-sidebar { + box-sizing: border-box; + width: 170px; + flex-grow: 0; + flex-shrink: 0; + align-self: flex-start; + + margin-left: 15px; + padding: $boxPadding; + + .rex-action-separator { + height: 10px; + } +} + +#rex-category-sidebar { + width: 240px; + flex-grow: 0; + flex-shrink: 0; + position: relative; + + border-right: $boxBorder; + padding: $boxTopPadding 0; + + background: #f8f8f8; + + & > ul { + margin-top: 0; + } + + .rex-nav-item { + cursor: pointer; + margin: 0; + padding: 3px $boxLeftPadding 3px $capViewContainerPaddingLeft; + } + + .rex-nav-item:hover { + background-color: #E5F3FF; + } + + .rex-selected-nav-item { + background-color: #CCE8FF; + box-shadow: 0px -1px 0px 0px #99D1FF, 0px 1px 0px 0px #99D1FF; + + //Don't change the color when hovering over a selected item. + &:hover { + background-color: #CCE8FF; + } + } + + $navItemLevelPadding: 13px; + @for $level from 2 through 5 { + .rex-nav-level-#{$level} { + padding-left: ($level - 2) * $navItemLevelPadding; + } + } + + .rex-nav-toggle { + visibility: hidden; + display: inline-block; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + + max-height: 100%; + width: 20px; + //background: #A27D35; + text-align: right; + vertical-align: middle; + + &:after { + font-family: dashicons, sans-serif; + content: "\f345"; + } + + &:hover { + color: #3ECEF9; + } + } + + .rex-nav-is-expanded .rex-nav-toggle { + //background: #00aa00; + &:after { + content: "\f347"; + } + } + + .rex-nav-has-children .rex-nav-toggle { + visibility: visible; + } + + .rex-dropdown-trigger { + position: absolute; + right: 0; + top: 0; + + padding: ($boxTopPadding + 2) ($boxLeftPadding + 2) 3px $boxLeftPadding; + } + + //Test styles for flexboxy appearance. + .rex-nav-item { + display: flex; + flex-wrap: nowrap; + align-items: baseline; + + height: 21px; + padding-top: 4px; + padding-bottom: 2px; + + $spaceWidth: 0.3em; + + .rex-nav-toggle { + flex-shrink: 0; + margin-right: $spaceWidth; + align-self: stretch; + padding: 1px 0; + } + + .rex-capability-count { + flex-shrink: 0; + margin-left: $spaceWidth; + margin-right: $spaceWidth; + } + + .rex-nav-item-header { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + } +} + +#rex-capability-view-container { + flex-grow: 70; + padding: $capViewContainerPaddingTop $capViewContainerPaddingLeft; + overflow-x: hidden; +} + +#rex-capability-view { + width: 100%; + + display: flex; + flex-direction: row; + flex-wrap: wrap; +} + +$oneCategoryWidth: 250px; +$categoryMargin: 8px; + +.rex-category { + box-sizing: border-box; + min-width: 160px; + width: $oneCategoryWidth; + + flex-grow: 0; + flex-shrink: 0; + flex-basis: auto; + + padding: 0; + margin: 0 $categoryMargin*2 $categoryMargin*2 0; + + border: 1px solid $amePostboxBorderColor; + + .rex-category-name { + font-weight: 600; + } + + .rex-category-subheading { + display: none; + color: #666; + font-size: 12px; + font-variant: small-caps; + } + + .rex-category-subtitle { + color: #888; + font-size: 0.95em; + font-family: Consolas, Monaco, monospace; + } + + .rex-category-contents { + box-sizing: border-box; + + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + + padding: 10px; + } + + &.rex-has-subcategories { + width: 100%; + flex-basis: 100%; + } + + .rex-category-header { + padding: 8px 10px; + border-bottom: 1px solid $amePostboxBorderColor; + } + + &.rex-top-category { + border: none; + margin: 0 0 10px 0; + padding: 0; + + & > .rex-category-header { + color: #23282d; + font-size: 1.3em; + margin: 1em 0; + padding: 0; + border-bottom: none; + } + + & > .rex-category-contents { + padding: 0; + } + } + + &.rex-sub-category { + box-shadow: $amePostboxShadow; + + & > .rex-category-header { + background: #fafafa; + } + } +} + +@function desiredCategoryWidth($columns) { + @return $oneCategoryWidth * $columns + ($categoryMargin * 2 * ($columns - 1)); +} + +$maxFixedColumns: 3; +@for $cols from 1 through $maxFixedColumns { + .rex-desired-columns-#{$cols} { + $desiredWidth: desiredCategoryWidth($cols); + width: $desiredWidth; + flex-grow: 0; + max-width: $desiredWidth * 2; + } +} + +.rex-desired-columns-max { + flex-basis: 100%; + width: 100%; +} + +//Switch fixed-size categories to full width on smaller screens. The breakpoints were +//determined empirically and might need to change if other parts of the UI change. +@media screen and (max-width: 1432px) { + $availableWidth: 780; + @for $cols from 2 through $maxFixedColumns { + $desiredWidth: desiredCategoryWidth($cols); + @if $desiredWidth >= $availableWidth { + .rex-desired-columns-#{$cols} { + flex-basis: 100%; + width: 100%; + } + } + } +} + +@media screen and (max-width: 1168px) { + $availableWidth: 516; + @for $cols from 2 through $maxFixedColumns { + $desiredWidth: desiredCategoryWidth($cols); + @if $desiredWidth >= $availableWidth { + .rex-desired-columns-#{$cols} { + flex-basis: 100%; + width: 100%; + } + } + } +} + +//region Full-width categories +//A "full-width" category spans the entire width of the container and displays permissions in columns. +.rex-full-width-categories { + .rex-category { + width: 100%; + max-width: unset; + } + + .rex-desired-columns-1 > .rex-category-contents > .rex-permission-list { + column-count: 1; + max-width: 300px; + } +} + +/* + * Ensure that each category contains no more than the desired number of columns. + * This is done by adding an invisible space filler element to the end of the permission list. + * + * Warning: This hack is not perfect. It can allow n+1 columns sometimes. + */ +$minPermissionsPerColumn: 3; //Note: This must match the minItemsPerColumn variable in role-editor.ts. + +@mixin addColumnFiller($expectedColumns) { + .rex-full-width-categories { + @for $cols from 2 through 5 { + @if $cols < $expectedColumns { + .rex-desired-columns-#{$cols} > .rex-category-contents > .rex-permission-list::after { + content: 'filler'; + display: block; + background: yellowgreen; + font-size: 13px; //Same as permission font size. Might be unnecessary. + + height: ($expectedColumns - $cols) * $minPermissionsPerColumn * $onePermissionHeight; + visibility: hidden; + } + } + } + } +} + +@media screen and (min-width: 1292px) and (max-width: 1501px) { + @include addColumnFiller(3); +} + +@media screen and (min-width: 1502px) and (max-width: 1711px) { + @include addColumnFiller(4); +} + +@media screen and (min-width: 1712px) { + @include addColumnFiller(5); +} + +//endregion + +.rex-show-category-subheadings .rex-category .rex-category-subheading { + display: block; + width: 100%; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.rex-capability-count { + //In the future, we could show the number of caps in a bubble and style it depending + //on how many capabilities are enabled (none/some/all). + //background-color: #eee; + //border: 1px solid #ddd; + //color: #666; + + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + + font-size: 12px; + //padding: 0 7px; + + &.rex-all-capabilities-enabled { + //font-weight: bold; + //color: #494; + //border-color: green; + //background-color: #ddffcc; + } + + &:before { + content: "("; + } + + &:after { + content: ")"; + } +} + +.rex-enabled-capability-count + .rex-total-capability-count { + &:before { + content: "/"; + } +} + +.rex-permission-list { + box-sizing: border-box; + width: 100%; + + columns: 200px; + column-gap: 10px; + + .rex-permission { + //margin-bottom: 6px; + //background: #fafafa; //For development. The color helps to visually estimate the size of the element. + } +} + +.rex-permission { + box-sizing: border-box; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + font-size: 13px; + height: $onePermissionHeight; + vertical-align: baseline; + + break-inside: avoid-column; + + display: flex; + + label, .rex-permission-tip-trigger { + vertical-align: baseline; + padding-top: 3px; + padding-bottom: 3px; + } + + label { + flex-grow: 1; + flex-shrink: 1; + flex-basis: 50px; + + overflow: hidden; + text-overflow: ellipsis; + } + + .rex-permission-tip-trigger { + flex-grow: 0; + flex-shrink: 0; + flex-basis: 20px; + } +} + +.rex-is-redundant { + color: #888; +} + +.rex-is-explicitly-denied input[type=checkbox] { + border-color: red; +} + +@mixin capCheckboxEffect($color) { + background-color: $color; +} + +.rex-is-personal-override { + &.rex-is-explicitly-denied input[type=checkbox] { + @include capCheckboxEffect(#ffe5e5); + } + + input[type=checkbox]:checked { + @include capCheckboxEffect(#d9ffd9); + border-color: green; + } +} + +.rex-is-inherited input[type=checkbox] { + +} + +//region Permission tooltips +.rex-permission-tip-trigger { + //We could hide the trigger by default, but I'm not sure if that's the best option. + visibility: hidden; + + display: inline-block; + min-width: 20px; + height: 100%; + margin: 0; + padding-left: 2px; + + cursor: pointer; + color: #777; //Unsure. Even this softer color still draws more attention than a tooltip should. + + &:hover { + color: #0096dd; + } +} + +.rex-permission:hover { + background-color: #fafafa; + + .rex-permission-tip-trigger { + visibility: visible; + } +} + +.rex-tooltip { + max-width: 700px; + + .rex-tooltip-section-container { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + } + + .rex-tooltip-section { + max-width: 400px; + } +} + +#rex-permission-tip { + overflow-y: auto; + max-height: 600px; + + h4 { + margin-bottom: 0.4em; + } + + .rex-tip-granted-permissions { + list-style: disc inside; + margin-top: 0; + margin-bottom: 0; + } + + .rex-documentation-link { + display: inline-block; + max-width: 100%; + overflow-wrap: break-word; + } +} + +.rex-capability-inheritance-breakdown { + @include ame-striped-table; + + .rex-is-decisive-actor { + td:first-child:after { + content: "\1f844"; //Wide left arrow. + display: inline-block; + font-weight: bold; + margin-left: 0.5em; + } + } +} + +//endregion + +#rex-view-toolbar { + background: #fcfcfc; //Consider #f5f5f5 and #fcfcfc as alternatives. + border-bottom: 1px solid #ddd; + + padding: 0 $boxLeftPadding $boxTopPadding $boxLeftPadding; + margin: (-$capViewContainerPaddingTop) (-$capViewContainerPaddingLeft) (0) (-$capViewContainerPaddingLeft); + + display: flex; + align-items: center; + flex-wrap: wrap; + + & > * { + margin-top: $boxTopPadding; + } + + .button { + vertical-align: middle; + } + + > label { + margin-right: 10px; + } + + .rex-dropdown-trigger .dashicons { + line-height: 1.3; //This isn't quite right, but it will do for now. + } +} + +#rex-quick-search-query { + min-width: 250px; + max-width: 100%; + margin-right: 10px; +} + +#rex-misc-view-options-button { + margin-left: auto; + margin-right: 10px; +} + +#rex-category-view-selector { + +} + + +.rex-search-highlight { + background-color: #ffff00; +} + +//region CPT & Taxonomy tables +.rex-permission-table { + th input[type="checkbox"] { + vertical-align: middle; + margin: -4px 4px -1px 0; + } + + @include ame-striped-table; + + td ul { + margin: 0; + } + + .rex-base-cap-notice { + color: #888; + } +} + +/* Switch to fixed layout in narrow viewports to prevent overflow. */ +@mixin fixedTableLayout($nameColumnWidth: 25%) { + table-layout: fixed; + max-width: 100%; + + .rex-category-name-column { + width: $nameColumnWidth; + } +} + +@media screen and (max-width: 1540px) { + .rex-permission-table { + @include fixedTableLayout(20%); + } + + .rex-readable-names-enabled .rex-permission-table { + @include fixedTableLayout(25%); + } +} + +/* The taxonomy table needs a wider screen because it has more columns. */ +@media screen and (max-width: 1650px) { + #rex-taxonomy-summary-category .rex-permission-table { + @include fixedTableLayout(25%); + } +} + +/* +When in "human readable" mode, the taxonomy table doesn't show capability names, +so it won't overflow its container unless the viewport is very small. +*/ +.rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table { + table-layout: auto; + max-width: 600px; + + .rex-capability-name, .rex-permission-tip-trigger { + display: none; + } + + .rex-permission, th[scope="col"] { + text-align: center; + } + + .rex-category-name-column { + width: unset; + } +} + +@media screen and (max-width: 1200px) { + .rex-readable-names-enabled #rex-taxonomy-summary-category .rex-permission-table { + @include fixedTableLayout(40%); + } +} + +//endregion + +#rex-action-sidebar { + .rex-action-button { + display: block; + margin-bottom: 4px; + width: 100%; + } +} + +#rex-permission-list-view { + column-width: 240px; + column-gap: 16px; + padding-top: $boxLeftPadding; +} + +#rex-category-view-spacer { + width: 100%; + height: $capViewContainerPaddingTop; +} + +.rex-dropdown-trigger { + display: inline-block; + box-sizing: border-box; + cursor: pointer; + + padding: 2px; + color: #aaa; + text-decoration: none; + + &:hover, &:focus { + color: #777; + text-decoration: none; + } +} + +.rex-dropdown { + position: absolute; + + border: $boxBorder; + background: #fff; + box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2); + + padding: $boxPadding; + z-index: 100; + + .rex-dropdown-item { + display: block; + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + } + + .rex-dropdown-sub-item { + margin-left: 1em; + } + + .rex-dropdown-item > .rex-dropdown-item { + margin-bottom: 6px; + + &:last-child { + margin-bottom: 0; + } + } +} + +.ui-dialog { + .ui-dialog-buttonpane { + background: #fcfcfc; + border-top: 1px solid #dfdfdf; + padding: 8px; + + &:after { + clear: both; + content: ""; + min-height: 0; + display: table; + border-collapse: collapse; + } + } + + //In WordPress the "Cancel" option is usually on the left side, + //but AME historically puts it on the right. + .ui-dialog-buttonset { + width: 100%; + + .ui-button.rex-dialog-cancel-button, .ui-button.ame-dialog-cancel-button { + float: right; + margin-right: 0 !important; + } + + .ui-button { + float: left; + } + } +} + +.rex-dialog { + input[type=text], select { + box-sizing: border-box; + display: block; + width: 100%; + } +} + +.rex-dialog-section { + margin-top: 0; +} + +#rex-delete-capability-dialog { + .rex-deletable-capability-container { + max-height: 400px; + overflow-y: auto; + } + + .rex-deletable-capability-list { + margin-top: 0; + list-style-type: none; + } +} + +#rex-add-capability-dialog { + #rex-new-capability-name { + box-sizing: border-box; + width: 100%; + } + + #rex-add-capability-validation-message { + min-height: 40px; + margin-bottom: 6px; + } +} + +#rex-delete-role-dialog { + .rex-deletable-role-list-container { + max-height: 380px; + + overflow-y: auto; + margin-top: 10px; + } + + .rex-deletable-role-list { + table-layout: fixed; + @include ame-striped-table; + } + + .rex-role-name-column > label { + display: inline-block; + width: 100%; + } + + .rex-role-usage-column { + width: 6em; + max-width: 30%; + color: #888; + text-align: right; + } +} + +//region Editable roles dialog +$editableRolesSectionBorder: #ccd0d4; +#rex-editable-roles-container { + display: flex; + + .ame-role-table { + min-width: 190px; + border: 1px solid $editableRolesSectionBorder; + border-right-style: none; + + td { + cursor: pointer; + } + } + + $selectedRowTipColor: white; + + .ame-selected-role-table-row { + background: #CCE8FF; + + .ame-selected-role-tip { + visibility: visible; + } + + .ame-column-role-name { + font-weight: bold; + } + } + + .ame-column-selected-role-tip { + position: relative; + padding: 0; + min-width: 30px; + } + + .ame-selected-role-tip { + visibility: hidden; + + height: 100%; + width: 100%; + box-sizing: border-box; + + position: absolute; + top: 0; + right: -2px; + + border-right: 1px solid $selectedRowTipColor; + + .ame-rex-svg-triangle { + box-sizing: border-box; + position: absolute; + right: 0; + height: 100%; + + polygon { + fill: $selectedRowTipColor; + stroke: $selectedRowTipColor; + stroke-width: 1px; + } + } + } +} + +#rex-editable-roles-options { + padding: 4px $boxTopPadding $boxTopPadding $boxTopPadding; + border: 1px solid $editableRolesSectionBorder; + + fieldset > p:first-of-type { + margin-top: 0; + } +} + +#rex-editable-role-list { + margin-left: 1em; + margin-top: 0; +} + +//endregion + +//region User role list +#rex-user-role-list { + border-right: $boxBorder; + padding: $boxPadding; + + background: #f8f8f8; + + p:first-child { + margin-top: 0; + } +} + +#rex-primary-user-role { + display: block; +} + +.rex-user-role-option-list { + margin-top: 0; +} +//endregion \ No newline at end of file diff --git a/extras/modules/role-editor/role-editor.ts b/extras/modules/role-editor/role-editor.ts new file mode 100644 index 0000000..8ae7a13 --- /dev/null +++ b/extras/modules/role-editor/role-editor.ts @@ -0,0 +1,3845 @@ +///+/// +/// +/// +/// +/// +/// +/// + +class RexPermission { + public readonly capability: RexCapability; + + labelHtml: KnockoutComputed ; + protected readableAction: string = null; + + mainDescription: string = ''; + + isRedundant: boolean = false; + + readonly isVisible: KnockoutComputed ; + + private editor: RexRoleEditor; + + constructor(editor: RexRoleEditor, capability: RexCapability) { + this.editor = editor; + this.capability = capability; + + const self = this; + + this.labelHtml = ko.pureComputed({ + read: self.getLabelHtml, + deferEvaluation: true, + owner: this + }); + //Prevent freezing when entering a search query. Highlighting keywords in hundreds of capabilities can be slow. + this.labelHtml.extend({rateLimit: {timeout: 50, method: "notifyWhenChangesStop"}}); + + this.isVisible = ko.computed({ + read: function () { + if (!editor.capabilityMatchesFilters(self.capability)) { + return false; + } + + //When in list view, check if the capability is inside the selected category. + if (editor.categoryViewMode() === RexRoleEditor.listView) { + if (!editor.isInSelectedCategory(self.capability.name)) { + return false; + } + } + + if (self.capability.isDeleted()) { + return false; + } + + return !(self.isRedundant && !editor.showRedundantEnabled()); + + }, + owner: this, + deferEvaluation: true + }); + } + + protected getLabelHtml(): string { + let text; + + if ((this.readableAction !== null) && this.editor.readableNamesEnabled()) { + text = this.readableAction; + } else { + text = this.capability.displayName(); + } + + let html = wsAmeLodash.escape(text); + if (this.isVisible()) { + html = this.editor.highlightSearchKeywords(html); + } + + //Let the browser break words on underscores. + html = html.replace(/_/g, '_ '); + + return html; + } +} + +interface RexComponentData { + name: string; + type?: string; + capabilityDocumentationUrl?: string; +} + +/** + * A basic representation of any component or extension that can add new capabilities. + * This includes plugins, themes, and the WordPress core. + */ +class RexWordPressComponent { + readonly name: string; + readonly id: string; + capabilityDocumentationUrl?: string; + + constructor(id: string, name: string) { + this.id = id; + this.name = name; + } + + static fromJs(id: string, details: RexComponentData): RexWordPressComponent { + const instance = new RexWordPressComponent(id, details.name ? details.name : id); + if (details.capabilityDocumentationUrl) { + instance.capabilityDocumentationUrl = details.capabilityDocumentationUrl; + } + return instance; + } +} + +class RexObservableCapabilityMap { + private readonly initialCapabilities: CapabilityMap; + private capabilities: { [capabilityName: string]: KnockoutObservable } = {}; + + constructor(initialCapabilities: CapabilityMap) { + if (initialCapabilities) { + this.initialCapabilities = wsAmeLodash.clone(initialCapabilities); + } else { + this.initialCapabilities = {}; + } + } + + getCapabilityState(capabilityName: string): boolean { + const observable = this.getObservable(capabilityName); + return observable(); + } + + setCapabilityState(capabilityName: string, state: boolean | null) { + const observable = this.getObservable(capabilityName); + observable(state); + } + + getAllCapabilities(): CapabilityMap { + const _ = wsAmeLodash; + + let result = this.initialCapabilities ? _.clone(this.initialCapabilities) : {}; + _.forEach(this.capabilities, function (observable, name) { + const isGranted = observable(); + if (isGranted === null) { + delete result[name]; + } else { + result[name] = isGranted; + } + }); + return result; + } + + private getObservable(capabilityName: string): KnockoutObservable { + if (!this.capabilities.hasOwnProperty(capabilityName)) { + let initialValue = null; + if (this.initialCapabilities.hasOwnProperty(capabilityName)) { + initialValue = this.initialCapabilities[capabilityName]; + } + this.capabilities[capabilityName] = ko.observable(initialValue); + } + return this.capabilities[capabilityName]; + } +} + +abstract class RexBaseActor implements IAmeActor { + /** + * Actor ID. Usually in the form of "prefix:name". + */ + id: KnockoutObservable ; + + /** + * Internal role name or user login. + */ + name: KnockoutObservable ; + + /** + * Formatted, human-readable name. + */ + displayName: KnockoutObservable ; + + canHaveRoles: boolean = false; + + private capabilities: RexObservableCapabilityMap; + + protected constructor(id: string, name: string, displayName: string, capabilities?: CapabilityMap) { + this.id = ko.observable(id); + this.name = ko.observable(name); + this.displayName = ko.observable(displayName); + this.capabilities = new RexObservableCapabilityMap(capabilities || {}); + } + + hasCap(capability: string): boolean { + return (this.capabilities.getCapabilityState(capability) === true); + } + + getCapabilityState(capability: string) { + return this.getOwnCapabilityState(capability); + } + + getOwnCapabilityState(capability: string): boolean | null { + return this.capabilities.getCapabilityState(capability); + } + + setCap(capability: string, enabled: boolean) { + this.capabilities.setCapabilityState(capability, enabled); + } + + deleteCap(capability: string) { + this.capabilities.setCapabilityState(capability, null); + } + + getDisplayName(): string { + return this.displayName(); + } + + getId(): string { + return this.id(); + } + + /** + * Get capabilities that are explicitly assigned/denied to this actor. + * Does not include capabilities that a user inherits from their role(s). + */ + getOwnCapabilities(): CapabilityMap { + return this.capabilities.getAllCapabilities(); + } +} + +class RexRole extends RexBaseActor { + static readonly builtInRoleNames = ['administrator', 'editor', 'author', 'subscriber', 'contributor']; + + hasUsers: boolean = false; + + public constructor(name: string, displayName: string, capabilities?: CapabilityMap) { + super('role:' + name, name, displayName, capabilities); + } + + public static fromRoleData(data: RexRoleData) { + const role = new RexRole(data.name, data.displayName, data.capabilities); + role.hasUsers = data.hasUsers; + return role; + } + + /** + * Is this one of the default roles that are part of WordPress core? + * + * Note: I'm calling this property "built-in" instead of "default" to distinguish it + * from the default role for new users. + */ + isBuiltIn() { + return RexRole.builtInRoleNames.indexOf(this.name()) >= 0; + } + + toJs(): RexStorableRoleData { + return { + name: this.name(), + displayName: this.displayName(), + capabilities: this.getOwnCapabilities() + }; + } +} + +class RexSuperAdmin extends RexBaseActor { + private static instance: RexSuperAdmin = null; + + protected constructor() { + super('special:super_admin', 'Super Admin', 'Super Admin'); + } + + static getInstance(): RexSuperAdmin { + if (RexSuperAdmin.instance === null) { + RexSuperAdmin.instance = new RexSuperAdmin(); + } + return RexSuperAdmin.instance; + } +} + +class RexUser extends RexBaseActor implements IAmeUser { + roles: KnockoutObservableArray ; + isSuperAdmin: boolean = false; + userLogin: string; + userId: number; + + constructor(login: string, displayName: string, capabilities?: CapabilityMap, userId?: number) { + super('user:' + login, login, displayName, capabilities); + this.userLogin = login; + this.canHaveRoles = true; + this.roles = ko.observableArray([]); + this.userId = userId; + } + + hasCap(capability: string, outGrantedBy?: RexBaseActor[]): boolean { + return (this.getCapabilityState(capability, outGrantedBy) === true); + } + + getCapabilityState(capability: string, outGrantedBy?: RexBaseActor[]): boolean { + if (capability === 'do_not_allow') { + return false; + } + + if (this.isSuperAdmin) { + if (outGrantedBy) { + outGrantedBy.push(RexSuperAdmin.getInstance()); + } + return (capability !== 'do_not_allow'); + } + + let result = super.getCapabilityState(capability); + if (result !== null) { + if (outGrantedBy) { + outGrantedBy.push(this); + } + return result; + } + + wsAmeLodash.each(this.roles(), (role) => { + const roleHasCap = role.getCapabilityState(capability); + if (roleHasCap !== null) { + if (outGrantedBy) { + outGrantedBy.push(role); + } + result = roleHasCap; + } + }); + + return result; + } + + // noinspection JSUnusedGlobalSymbols Used in KO templates. + getInheritanceDetails(capability: RexCapability): any[] { + const _ = wsAmeLodash; + let results = []; + //Note: Alternative terms include "Assigned", "Granted", "Yes"/"No". + + if (this.isSuperAdmin) { + const superAdmin = RexSuperAdmin.getInstance(); + let description = 'Allow everything'; + if (capability.name === 'do_not_allow') { + description = 'Deny'; + } + results.push({ + actor: superAdmin, + name: superAdmin.displayName(), + description: description + }); + } + + _.each(this.roles(), (role) => { + const roleHasCap = role.getCapabilityState(capability.name); + let description; + if (roleHasCap) { + description = 'Allow'; + } else if (roleHasCap === null) { + description = '—'; + } else { + description = 'Deny'; + } + results.push({ + actor: role, + name: role.displayName(), + description: description, + }); + }); + + let hasOwnCap = super.getCapabilityState(capability.name); + let description; + if (hasOwnCap) { + description = 'Allow'; + } else if (hasOwnCap === null) { + description = '—'; + } else { + description = 'Deny'; + } + results.push({ + actor: this, + name: 'User-specific setting', + description: description, + }); + + let relevantActors = []; + this.getCapabilityState(capability.name, relevantActors); + const decidingActor = _.last(relevantActors); + _.each(results, function (item) { + item.isDecisive = (item.actor === decidingActor); + }); + + return results; + } + + static fromAmeUser(data: AmeUser, editor: RexRoleEditor) { + const user = new RexUser(data.userLogin, data.displayName, data.capabilities, data.userId); + wsAmeLodash.forEach(data.roles, function (roleId) { + const role = editor.getRole(roleId); + if (role) { + user.roles.push(role); + } + }); + return user; + } + + static fromAmeUserProperties(properties: AmeUserPropertyMap, editor: RexRoleEditor) { + const user = new RexUser(properties.user_login, properties.display_name, properties.capabilities); + if (properties.id) { + user.userId = properties.id; + } + wsAmeLodash.forEach(properties.roles, function (roleId) { + const role = editor.getRole(roleId); + if (role) { + user.roles.push(role); + } + }); + return user; + } + + toJs(): RexStorableUserData { + const _ = wsAmeLodash; + let roles = _.invoke(this.roles(), 'name'); + return { + userId: this.userId, + userLogin: this.userLogin, + displayName: this.displayName(), + capabilities: this.getOwnCapabilities(), + roles: roles + }; + } +} + +interface RexStorableRoleData { + name: string; + displayName: string; + capabilities: CapabilityMap; +} + +interface RexRoleData extends RexStorableRoleData { + hasUsers: boolean; +} + +interface RexStorableUserData { + userId?: number; + userLogin: string; + displayName: string; + roles: string[]; + capabilities: CapabilityMap; +} + +interface RexUserData extends RexStorableUserData { + isSuperAdmin: boolean; +} + +type RexCategoryComparisonCallback = (a: RexCategory, b: RexCategory) => number; + +class RexCategory { + slug: string = null; + + name: string; + permissions: KnockoutObservableArray ; + origin: RexWordPressComponent = null; + subtitle: string = null; + htmlId: string = null; + + parent: RexCategory = null; + subheading: KnockoutObservable ; + subcategories: RexCategory[] = []; + + sortedSubcategories: KnockoutComputed ; + static readonly defaultSubcategoryComparison: RexCategoryComparisonCallback = function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }; + + navSubcategories: KnockoutComputed ; + protected subcategoryModificationFlag: KnockoutObservable ; + + protected editor: RexRoleEditor; + + areAllPermissionsEnabled: KnockoutComputed ; + areAnyPermissionsEditable: KnockoutComputed ; + + /** + * Number of unique capabilities in this category and all of its subcategories. + */ + totalCapabilityCount: KnockoutComputed ; + /** + * Number of capabilities that are granted to the currently selected actor. + */ + enabledCapabilityCount: KnockoutComputed ; + + isVisible: KnockoutComputed ; + isSelected: KnockoutObservable ; + desiredColumnCount: KnockoutComputed ; + + isNavExpanded: KnockoutObservable ; + isNavVisible: KnockoutComputed ; + + cssClasses: KnockoutComputed ; + navCssClasses: KnockoutComputed ; + readonly nestingDepth: KnockoutComputed ; + contentTemplate: KnockoutObservable ; + + isCapCountVisible: KnockoutComputed ; + isEnabledCapCountVisible: KnockoutComputed ; + + protected duplicates: RexCategory[] = []; + + constructor(name: string, editor: RexRoleEditor, slug: string = null, capabilities: string[] = []) { + const _ = wsAmeLodash; + const self = this; + this.editor = editor; + + this.name = name; + this.slug = slug; + if ((this.slug !== null) && (this.slug !== '')) { + editor.categoriesBySlug[this.slug] = this; + } + + let initialPermissions = _.map(capabilities, (capabilityName) => { + return new RexPermission(editor, editor.getCapability(capabilityName)); + }); + this.permissions = ko.observableArray(initialPermissions); + this.sortPermissions(); + this.contentTemplate = ko.observable('rex-default-category-content-template'); + + this.isSelected = ko.observable(false); + + this.enabledCapabilityCount = ko.pureComputed({ + read: function () { + return self.countUniqueCapabilities({}, function (capability: RexCapability) { + return capability.isEnabledForSelectedActor(); + }); + }, + deferEvaluation: true, + owner: this + }); + this.enabledCapabilityCount.extend({rateLimit: {timeout: 5, method: "notifyWhenChangesStop"}}); + + this.totalCapabilityCount = ko.pureComputed({ + read: function () { + return self.countUniqueCapabilities(); + }, + deferEvaluation: true, + owner: this + }); + + this.isCapCountVisible = ko.pureComputed({ + read: function () { + if (!editor.showNumberOfCapsEnabled()) { + return false; + } + + const totalCaps = self.totalCapabilityCount(), + enabledCaps = self.enabledCapabilityCount(); + if (!editor.showZerosEnabled() && ((totalCaps === 0) || (enabledCaps === 0))) { + return false; + } + + return editor.showTotalCapCountEnabled() || self.isEnabledCapCountVisible(); + }, + deferEvaluation: true, + owner: this + }); + this.isEnabledCapCountVisible = ko.pureComputed({ + read: function () { + if (!editor.showGrantedCapCountEnabled()) { + return false; + } + return (self.enabledCapabilityCount() > 0) || editor.showZerosEnabled(); + }, + deferEvaluation: true, + owner: this + }); + + this.areAllPermissionsEnabled = ko.computed({ + read: function () { + const items = self.permissions(); + const len = items.length; + + for (let i = 0; i < len; i++) { + if (!items[i].capability.isEnabledForSelectedActor() && items[i].capability.isEditable()) { + return false; + } + } + + for (let i = 0; i < self.subcategories.length; i++) { + if (!self.subcategories[i].areAllPermissionsEnabled()) { + return false; + } + } + + return true; + }, + write: function (enabled) { + const items = self.permissions(); + for (let i = 0; i < items.length; i++) { + let item = items[i]; + if (item.capability.isEditable()) { + item.capability.isEnabledForSelectedActor(enabled); + } + } + + for (let i = 0; i < self.subcategories.length; i++) { + self.subcategories[i].areAllPermissionsEnabled(enabled); + } + }, + deferEvaluation: true, + owner: this + }); + this.areAllPermissionsEnabled.extend({rateLimit: {timeout: 5, method: 'notifyWhenChangesStop'}}); + + this.areAnyPermissionsEditable = ko.pureComputed({ + read: () => { + const items = self.permissions(); + const len = items.length; + + for (let i = 0; i < len; i++) { + if (items[i].capability.isEditable()) { + return true; + } + } + + for (let i = 0; i < self.subcategories.length; i++) { + if (!self.subcategories[i].areAnyPermissionsEditable()) { + return true; + } + } + + return false; + }, + deferEvaluation: true, + owner: this + }); + this.areAnyPermissionsEditable.extend({rateLimit: {timeout: 5, method: 'notifyWhenChangesStop'}}); + + this.isVisible = ko.computed({ + read: function () { + let visible = false; + + let hasVisibleSubcategories = false; + _.forEach(self.subcategories, function (category) { + if (category.isVisible()) { + hasVisibleSubcategories = true; + return false; + } + }); + + //Hide it if not inside the selected category. + let isInSelectedCategory = false, + temp: RexCategory = self; + while (temp !== null) { + if (temp.isSelected()) { + isInSelectedCategory = true; + break; + } + temp = temp.parent; + } + + //In single-category view, the category also counts as "selected" + //if one of its duplicates is selected. + if ( + !isInSelectedCategory + && (self.duplicates.length > 0) + && (editor.categoryViewMode() === RexRoleEditor.singleCategoryView) + ) { + for (let i = 0; i < self.duplicates.length; i++) { + temp = self.duplicates[i]; + while (temp !== null) { + if (temp.isSelected()) { + isInSelectedCategory = true; + break; + } + temp = temp.parent; + } + if (isInSelectedCategory) { + break; + } + } + } + + if (!isInSelectedCategory && !hasVisibleSubcategories) { + return false; + } + + //Stay visible as long as at least one subcategory or permission is visible. + visible = hasVisibleSubcategories; + _.forEach(self.permissions(), function (permission) { + if (permission.isVisible()) { + visible = true; + return false; + } + }); + + return visible; + }, + deferEvaluation: true, + owner: this, + }); + this.isVisible.extend({ + rateLimit: { + timeout: 10, + method: 'notifyWhenChangesStop' + } + }); + + this.desiredColumnCount = ko.computed({ + read: function () { + let visiblePermissions = 0; + _.forEach(self.permissions(), function (permission) { + if (permission.isVisible()) { + visiblePermissions++; + } + }); + + let minItemsPerColumn = 12; + if (editor.categoryWidthMode() === 'full') { + minItemsPerColumn = 3; + } + + let desiredColumns = Math.max(Math.ceil(visiblePermissions / minItemsPerColumn), 1); + //Avoid situations where the last column has only one item (an orphan). + if ((desiredColumns >= 2) && (visiblePermissions % minItemsPerColumn === 1)) { + desiredColumns--; + } + if (desiredColumns > 3) { + return 'max'; + } + + return desiredColumns.toString(10); + }, + deferEvaluation: true + }); + + this.nestingDepth = ko.pureComputed({ + read: function () { + if (self.parent !== null) { + return self.parent.nestingDepth() + 1; + } + return 1; + }, + deferEvaluation: true + }); + + this.isNavExpanded = ko.observable( + (this.slug !== null) ? !editor.userPreferences.collapsedCategories.peek(this.slug) : true + ); + + if (this.slug) { + this.isNavExpanded.subscribe((newValue: boolean) => { + editor.userPreferences.collapsedCategories.toggle(this.slug, !newValue); + }); + } + + this.isNavVisible = ko.pureComputed({ + read: function () { + if (self.parent === null) { + return true; + } + return self.parent.isNavVisible() && self.parent.isNavExpanded(); + //Idea: We could hide it if all of the capabilities it contains have been deleted. + }, + deferEvaluation: true + }); + + this.cssClasses = ko.computed({ + read: function () { + let classes = []; + if (self.subcategories.length > 0) { + classes.push('rex-has-subcategories'); + } + if (self.parent) { + if (self.parent === editor.rootCategory) { + classes.push('rex-top-category'); + } else { + classes.push('rex-sub-category'); + } + } + + if (self.permissions().length > 0) { + classes.push('rex-desired-columns-' + self.desiredColumnCount()); + } + return classes.join(' '); + }, + deferEvaluation: true + }); + + this.navCssClasses = ko.pureComputed({ + read: function () { + let classes = []; + if (self.isSelected()) { + classes.push('rex-selected-nav-item'); + } + if (self.isNavExpanded()) { + classes.push('rex-nav-is-expanded'); + } + if (self.subcategories.length > 0) { + classes.push('rex-nav-has-children'); + } + classes.push('rex-nav-level-' + self.nestingDepth()); + return classes.join(' '); + }, + deferEvaluation: true + }); + + this.subcategoryModificationFlag = ko.observable(this.subcategories.length); + this.sortedSubcategories = ko.pureComputed({ + read: () => { + //Refresh the sorted list when categories are added or removed. + this.subcategoryModificationFlag(); + return this.getSortedSubcategories(); + }, + deferEvaluation: true + }); + + this.navSubcategories = ko.pureComputed({ + read: () => { + this.subcategoryModificationFlag(); + return this.subcategories; + }, + deferEvaluation: true + }); + + this.subheading = ko.pureComputed({ + read: () => { + return this.getSubheadingItems().join(', '); + }, + deferEvaluation: true + }); + } + + addSubcategory(category: RexCategory, afterName?: string) { + category.parent = this; + if (afterName) { + const index = wsAmeLodash.findIndex(this.subcategories, {'name': afterName}); + if (index > -1) { + this.subcategories.splice(index + 1, 0, category); + this.subcategoryModificationFlag(this.subcategories.length); + return; + } + } + this.subcategories.push(category); + this.subcategoryModificationFlag(this.subcategories.length); + } + + // noinspection JSUnusedGlobalSymbols Used in KO templates. + toggleSubcategories() { + this.isNavExpanded(!this.isNavExpanded()); + } + + protected getSortedSubcategories(): RexCategory[] { + //In most cases, the subcategory list is already sorted either alphabetically or in a predefined order + //chosen for specific category. Subcategories can override this method to change the sort order. + return this.subcategories; + } + + /** + * Sort the permissions in this category. Doesn't affect subcategories. + * The default sort is alphabetical, but subclasses can override this method to specify a custom order. + */ + sortPermissions() { + this.permissions.sort(function (a, b) { + return a.capability.name.toLowerCase().localeCompare(b.capability.name.toLowerCase()); + }); + } + + countUniqueCapabilities(accumulator: AmeDictionary = {}, predicate: Function = null): number { + let total = 0; + const permissions = this.permissions(); + + for (let i = 0; i < permissions.length; i++) { + const capability = permissions[i].capability; + if (accumulator.hasOwnProperty(capability.name)) { + continue; + } + if (predicate && !predicate(capability)) { + continue; + } + if (capability.isDeleted()) { + continue; + } + + accumulator[capability.name] = true; + total++; + } + + for (let i = 0; i < this.subcategories.length; i++) { + total = total + this.subcategories[i].countUniqueCapabilities(accumulator, predicate); + } + + return total; + } + + protected findCategoryBySlug(slug: string): RexCategory { + if (this.editor.categoriesBySlug.hasOwnProperty(slug)) { + return this.editor.categoriesBySlug[slug]; + } + return null; + } + + static fromJs(details: RexCategoryData, editor: RexRoleEditor): RexCategory { + let category; + if (details.variant && details.variant === 'post_type') { + category = new RexPostTypeCategory(details.name, editor, details.contentTypeId, details.slug, details.permissions); + } else if (details.variant && details.variant === 'taxonomy') { + category = new RexTaxonomyCategory(details.name, editor, details.contentTypeId, details.slug, details.permissions); + } else { + category = new RexCategory(details.name, editor, details.slug, details.capabilities); + } + + if (details.componentId) { + category.origin = editor.getComponent(details.componentId); + } + + if (details.subcategories) { + wsAmeLodash.forEach(details.subcategories, (childDetails) => { + const subcategory = RexCategory.fromJs(childDetails, editor); + category.addSubcategory(subcategory); + }); + } + + return category; + } + + usesBaseCapabilities(): boolean { + return false; + } + + getDeDuplicationKey(): string { + let key = this.slug ?? this.name; + if (this.parent) { + key = this.parent.getDeDuplicationKey() + '>' + key; + } + return key; + } + + addDuplicate(category: RexCategory) { + if (this.duplicates.indexOf(category) === -1) { + this.duplicates.push(category); + } + } + + protected getSubheadingItems(): string[] { + let items = []; + if (this.parent !== null) { + items.push(this.parent.name); + } + if (this.duplicates.length > 0) { + for (let i = 0; i < this.duplicates.length; i++) { + let category = this.duplicates[i]; + if (category.parent) { + items.push(category.parent.name); + } + } + } + return items; + } + + getAbsoluteName() { + let components = [this.name]; + let parent = this.parent; + while (parent !== null) { + components.unshift(parent.name); + parent = parent.parent; + } + return components.join(' > '); + } +} + +interface RexContentTypePermission extends RexPermission { + readonly action: string; +} + +abstract class RexContentTypeCategory extends RexCategory { + public actions: { [action: string]: RexContentTypePermission } = {}; + protected baseCategorySlug: string = null; + isBaseCapNoticeVisible: KnockoutComputed ; + + protected constructor(name: string, editor: RexRoleEditor, slug: string = null) { + super(name, editor, slug); + + this.isBaseCapNoticeVisible = ko.pureComputed({ + read: () => { + if (editor.showBaseCapsEnabled()) { + return false; + } + return this.usesBaseCapabilities(); + }, + deferEvaluation: true + }); + } + + /** + * Check if the post type or taxonomy represented by this category uses the same capabilities + * as the built-in "post" type or the "category" taxonomy. + */ + usesBaseCapabilities(): boolean { + const baseCategory = this.getBaseCategory(); + if (baseCategory === null || this === baseCategory) { + return false; + } + + let allCapsMatch = true; + wsAmeLodash.forEach(this.actions, function (item) { + let isMatch = item.action + && baseCategory.actions.hasOwnProperty(item.action) + && (item.capability === baseCategory.actions[item.action].capability); + + if (!isMatch) { + allCapsMatch = false; + return false; + } + }); + return allCapsMatch; + } + + protected getBaseCategory(): RexContentTypeCategory { + if (this.baseCategorySlug !== null) { + let result = this.findCategoryBySlug(this.baseCategorySlug); + if (result instanceof RexContentTypeCategory) { + return result; + } + } + return null; + } +} + +class RexPostTypePermission extends RexPermission implements RexContentTypePermission { + public readonly action: string; + + public static readonly actionDescriptions: { [cptAction: string]: string } = { + 'edit_and_create': 'Edit and create %s', + 'edit_posts': 'Edit %s', + 'create_posts': 'Create new %s', + 'edit_published_posts': 'Edit published %s', + 'edit_others_posts': 'Edit %s created by others', + 'edit_private_posts': 'Edit private %s created by others', + 'publish_posts': 'Publish %s', + 'read_private_posts': 'Read private %s', + 'delete_posts': 'Delete %s', + 'delete_published_posts': 'Delete published %s', + 'delete_others_posts': 'Delete %s created by others', + 'delete_private_posts': 'Delete private %s created by others', + }; + + constructor(editor: RexRoleEditor, capability: RexCapability, action: string, pluralNoun: string = '') { + super(editor, capability); + + this.action = action; + this.readableAction = wsAmeLodash.capitalize(this.action.replace('_posts', '').replace('_', ' ')); + + if (RexPostTypePermission.actionDescriptions.hasOwnProperty(action) && pluralNoun) { + this.mainDescription = RexPostTypePermission.actionDescriptions[action].replace('%s', pluralNoun); + } + } +} + +class RexPostTypeCategory extends RexContentTypeCategory { + readonly pluralLabel: string = ''; + + public actions: { [action: string]: RexPostTypePermission } = {}; + public readonly postType: string; + public readonly isDefault: boolean; + + private static readonly desiredActionOrder: { [cptAction: string]: number } = { + 'edit_posts': 1, + 'edit_others_posts': 2, + 'edit_published_posts': 3, + 'edit_private_posts': 4, + 'publish_posts': 5, + 'delete_posts': 6, + 'delete_others_posts': 7, + 'delete_published_posts': 8, + 'delete_private_posts': 9, + 'read_private_posts': 10, + 'create_posts': 11, + }; + + constructor( + name: string, + editor: RexRoleEditor, + postTypeId: string, + slug: string = null, + permissions: { [action: string]: string }, + isDefault: boolean = false + ) { + super(name, editor, slug); + const _ = wsAmeLodash; + + this.baseCategorySlug = 'postTypes/post'; + this.postType = postTypeId; + this.isDefault = isDefault; + this.subtitle = this.postType; + if (editor.postTypes[postTypeId].pluralLabel) { + this.pluralLabel = editor.postTypes[postTypeId].pluralLabel; + } else { + this.pluralLabel = name.toLowerCase(); + } + + this.permissions = ko.observableArray(_.map(permissions, (capability, action) => { + const permission = new RexPostTypePermission(editor, editor.getCapability(capability), action, this.pluralLabel); + + //The "read" capability is already shown in the core category and every role has it by default. + if (capability === 'read') { + permission.isRedundant = true; + } + + this.actions[action] = permission; + return permission; + })); + + this.sortPermissions(); + + //The "create" capability is often the same as the "edit" capability. + const editPerm = _.get(this.actions, 'edit_posts', null), + createPerm = _.get(this.actions, 'create_posts', null); + if (editPerm && createPerm && (createPerm.capability.name === editPerm.capability.name)) { + createPerm.isRedundant = true; + } + } + + + getDeDuplicationKey(): string { + return 'postType:' + this.postType; + } + + sortPermissions() { + this.permissions.sort(function (a: RexPostTypePermission, b: RexPostTypePermission) { + const priorityA = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexPostTypeCategory.desiredActionOrder[a.action] : 1000; + const priorityB = RexPostTypeCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexPostTypeCategory.desiredActionOrder[b.action] : 1000; + + let delta = priorityA - priorityB; + if (delta !== 0) { + return delta; + } + + return a.capability.name.localeCompare(b.capability.name); + }); + } + + protected getSubheadingItems(): string[] { + let items = super.getSubheadingItems(); + items.push(this.postType); + return items; + } +} + +class RexTaxonomyPermission extends RexPermission implements RexContentTypePermission { + public readonly action: string; + + public static readonly actionDescriptions: { [cptAction: string]: string } = { + 'manage_terms': 'Manage %s', + 'edit_terms': 'Edit %s', + 'delete_terms': 'Delete %s', + 'assign_terms': 'Assign %s', + }; + + constructor(editor: RexRoleEditor, capability: RexCapability, action: string, pluralNoun: string = '') { + super(editor, capability); + + this.action = action; + this.readableAction = wsAmeLodash.capitalize(this.action.replace('_terms', '').replace('_', ' ')); + + if (RexTaxonomyPermission.actionDescriptions.hasOwnProperty(action) && pluralNoun) { + this.mainDescription = RexTaxonomyPermission.actionDescriptions[action].replace('%s', pluralNoun); + } + } +} + +class RexTaxonomyCategory extends RexContentTypeCategory { + public actions: { [action: string]: RexTaxonomyPermission } = {}; + private readonly taxonomy: string; + + private static readonly desiredActionOrder: { [cptAction: string]: number } = { + 'manage_terms': 1, + 'edit_terms': 2, + 'delete_terms': 3, + 'assign_terms': 4, + }; + + constructor( + name: string, + editor: RexRoleEditor, + taxonomyId: string, + slug: string = null, + permissions: { [action: string]: string } + ) { + super(name, editor, slug); + const _ = wsAmeLodash; + + this.baseCategorySlug = 'taxonomies/category'; + this.taxonomy = taxonomyId; + this.subtitle = taxonomyId; + + const noun = name.toLowerCase(); + this.permissions = ko.observableArray(_.map(permissions, (capability, action) => { + const permission = new RexTaxonomyPermission(editor, editor.getCapability(capability), action, noun); + this.actions[action] = permission; + return permission; + })); + + this.sortPermissions(); + + //Permissions that use the same capability as the "manage_terms" permission are redundant. + if (this.actions.manage_terms) { + const manageCap = this.actions.manage_terms.capability.name; + for (let action in this.actions) { + if (!this.actions.hasOwnProperty(action)) { + continue; + } + if ((action !== 'manage_terms') && (this.actions[action].capability.name === manageCap)) { + this.actions[action].isRedundant = true; + } + } + } + } + + getDeDuplicationKey(): string { + return 'taxonomy:' + this.taxonomy; + } + + sortPermissions(): void { + this.permissions.sort(function (a: RexTaxonomyPermission, b: RexTaxonomyPermission) { + const priorityA = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(a.action) ? RexTaxonomyCategory.desiredActionOrder[a.action] : 1000; + const priorityB = RexTaxonomyCategory.desiredActionOrder.hasOwnProperty(b.action) ? RexTaxonomyCategory.desiredActionOrder[b.action] : 1000; + + let delta = priorityA - priorityB; + if (delta !== 0) { + return delta; + } + + return a.capability.name.localeCompare(b.capability.name); + }); + } + + protected getSubheadingItems(): string[] { + let items = super.getSubheadingItems(); + items.push(this.taxonomy); + return items; + } +} + +interface RexPermissionTableColumn { + title: string; + actions: string[]; +} + +class RexTableViewCategory extends RexCategory { + tableColumns: KnockoutComputed ; + subcategoryComparisonCallback: RexCategoryComparisonCallback = null; + + constructor(name: string, editor: RexRoleEditor, slug: string = null) { + super(name, editor, slug); + + this.contentTemplate = ko.pureComputed(function () { + if (editor.categoryViewMode() === RexRoleEditor.hierarchyView) { + return 'rex-permission-table-template'; + } + return 'rex-default-category-content-template'; + }); + + this.subcategoryComparisonCallback = RexCategory.defaultSubcategoryComparison; + } + + protected getSortedSubcategories(): RexCategory[] { + if (this.editor.showBaseCapsEnabled()) { + return super.getSortedSubcategories(); + } + + let cats = wsAmeLodash.clone(this.subcategories); + return cats.sort((a, b) => { + //Special case: Put categories that use base capabilities at the end. + const aEqualsBase = a.usesBaseCapabilities(); + const bEqualsBase = b.usesBaseCapabilities(); + if (aEqualsBase && !bEqualsBase) { + return 1; + } else if (!aEqualsBase && bEqualsBase) { + return -1; + } + + //Otherwise just sort in the default order. + return this.subcategoryComparisonCallback(a, b); + }); + } + + /** + * Sort the underlying category array. + */ + public sortSubcategories() { + this.subcategories.sort(this.subcategoryComparisonCallback); + } +} + +class RexTaxonomyContainerCategory extends RexTableViewCategory { + constructor(name: string, editor: RexRoleEditor, slug: string = null) { + super(name, editor, slug); + + this.htmlId = 'rex-taxonomy-summary-category'; + + this.tableColumns = ko.pureComputed({ + read: () => { + const _ = wsAmeLodash; + const defaultTaxonomyActions = ['manage_terms', 'assign_terms', 'edit_terms', 'delete_terms']; + + let columns = [ + { + title: 'Manage', + actions: ['manage_terms'] + }, + { + title: 'Assign', + actions: ['assign_terms'] + }, + { + title: 'Edit', + actions: ['edit_terms'] + }, + { + title: 'Delete', + actions: ['delete_terms'] + } + ]; + let misColumnExists = false, miscColumn: RexPermissionTableColumn = null; + + for (let i = 0; i < this.subcategories.length; i++) { + const category = this.subcategories[i]; + if (!(category instanceof RexTaxonomyCategory)) { + continue; + } + + //Display any unrecognized actions in a "Misc" column. + const customActions = _.omit(category.actions, defaultTaxonomyActions); + if (!_.isEmpty(customActions)) { + if (!misColumnExists) { + miscColumn = {title: 'Misc', actions: []}; + columns.push(miscColumn); + } + miscColumn.actions = _.union(miscColumn.actions, _.keys(customActions)); + } + } + + return columns; + }, + deferEvaluation: true, + }); + } +} + +class RexPostTypeContainerCategory extends RexTableViewCategory { + constructor(name: string, editor: RexRoleEditor, slug: string = null) { + super(name, editor, slug); + + /* Note: This seems like poor design because the superclass overrides subclass + * behaviour (subcategory comparison) in some situations. Unfortunately, I haven't + * come up with anything better so far. Might be something to revisit later. + */ + + this.subcategoryComparisonCallback = function (a: RexPostTypeCategory, b: RexPostTypeCategory) { + //Special case: Put "Posts" at the top. + if (a.postType === 'post') { + return -1; + } else if (b.postType === 'post') { + return 1; + } + + //Put other built-in post types above custom post types. + if (a.isDefault && !b.isDefault) { + return -1; + } else if (b.isDefault && !a.isDefault) { + return 1; + } + + let labelA = a.name.toLowerCase(), labelB = b.name.toLowerCase(); + return labelA.localeCompare(labelB); + }; + + this.tableColumns = ko.pureComputed({ + read: () => { + const _ = wsAmeLodash; + const defaultPostTypeActions = _.keys(RexPostTypePermission.actionDescriptions); + + let columns = [ + { + title: 'Own items', + actions: ['create_posts', 'edit_posts', 'delete_posts', 'publish_posts', 'edit_published_posts', 'delete_published_posts'] + }, + { + title: 'Other\'s items', + actions: ['edit_others_posts', 'delete_others_posts', 'edit_private_posts', 'delete_private_posts', 'read_private_posts'] + } + ]; + let metaColumn = { + title: 'Meta', + actions: ['edit_post', 'delete_post', 'read_post'] + }; + columns.push(metaColumn); + + for (let i = 0; i < this.subcategories.length; i++) { + const category = this.subcategories[i]; + if (!(category instanceof RexPostTypeCategory)) { + continue; + } + + //Display any unrecognized actions in a "Misc" column. + const customActions = _.omit(category.actions, defaultPostTypeActions); + if (!_.isEmpty(customActions)) { + metaColumn.actions = _.union(metaColumn.actions, _.keys(customActions)); + } + } + + return columns; + }, + deferEvaluation: true, + }); + } +} + + +interface RexCapabilityData { + componentId?: string; + menuItems: string[]; + usedByComponents: string[]; + + documentationUrl?: string; + permissions?: string[]; + readableName?: string; +} + +class RexCapability { + readonly name: string; + + private readableName: string; + readonly displayName: KnockoutComputed ; + + private readonly editor: RexRoleEditor; + + readonly isEnabledForSelectedActor: KnockoutComputed ; + isEditable: KnockoutComputed ; + + readonly responsibleActors: KnockoutComputed ; + readonly isInherited: KnockoutComputed ; + readonly isPersonalOverride: KnockoutComputed ; + readonly isExplicitlyDenied: KnockoutComputed ; + + originComponent: RexWordPressComponent = null; + usedByComponents: RexWordPressComponent[] = []; + + menuItems: string[] = []; + usedByPostTypeActions: { [postType: string]: { [action: string]: boolean } } = {}; + usedByTaxonomyActions: { [taxonomy: string]: { [action: string]: boolean } } = {}; + predefinedPermissions: string[] = []; + + grantedPermissions: KnockoutComputed ; + protected documentationUrl: string = null; + notes: string = null; + + readonly isDeleted: KnockoutObservable ; + + constructor(name: string, editor: RexRoleEditor) { + this.name = String(name); + this.editor = editor; + + const self = this; + + this.readableName = wsAmeLodash.capitalize(this.name.replace(/[_\-\s]+/g, ' ')); + this.displayName = ko.pureComputed({ + read: function () { + return editor.readableNamesEnabled() ? self.readableName : self.name; + }, + deferEvaluation: true, + owner: this + }); + this.isDeleted = ko.observable(false); + + this.responsibleActors = ko.computed({ + read: function () { + let actor = editor.selectedActor(), list = []; + if (actor instanceof RexUser) { + actor.hasCap(self.name, list); + } + return list; + }, + owner: this, + deferEvaluation: true + }); + + this.isInherited = ko.computed({ + read: function () { + const actor = editor.selectedActor(); + if (!actor.canHaveRoles) { + return false; + } + + const responsibleActors = self.responsibleActors(); + return responsibleActors + && (responsibleActors.length > 0) + && (wsAmeLodash.indexOf(responsibleActors, actor) < (responsibleActors.length - 1)) + }, + owner: this, + deferEvaluation: true + }); + + this.isPersonalOverride = ko.pureComputed({ + read: function () { + //This flag applies only to actors that can inherit permissions. + const actor = editor.selectedActor(); + if (!actor.canHaveRoles) { + return false; + } + return !self.isInherited(); + }, + owner: this, + deferEvaluation: true + }); + + this.isEditable = ko.pureComputed({ + read: function () { + if (self.isInherited() && !editor.inheritanceOverrideEnabled()) { + return false; + } + + return !self.isDeleted(); + }, + deferEvaluation: true + }); + + this.isEnabledForSelectedActor = ko.computed({ + read: function () { + return editor.selectedActor().hasCap(self.name); + }, + write: function (newState: boolean) { + const actor = editor.selectedActor(); + if (editor.isShiftKeyDown()) { + //Hold the shift key while clicking to cycle the capability between 3 states: + //Granted -> Denied -> Not granted. + const oldState = actor.getOwnCapabilityState(self.name); + if (newState) { + if (oldState === false) { + actor.deleteCap(self.name); //Denied -> Not granted. + } else if (oldState === null) { + actor.setCap(self.name, true); //Not granted -> Granted. + } + } else { + if (oldState === true) { + actor.setCap(self.name, false); //Granted -> Denied. + } else if (oldState === null) { + actor.setCap(self.name, true); //Not granted (inherited = Granted) -> Granted. + } + } + //Update the checkbox state. + if (actor.hasCap(self.name) !== newState) { + self.isEnabledForSelectedActor.notifySubscribers(); + } + return; + } + + if (newState) { + //TODO: If it's a user and the cap is explicitly negated, consider removing that state. + actor.setCap(self.name, newState); + } else { + //The default is to remove the capability instead of explicitly setting it to false. + actor.deleteCap(self.name); + + //If we're removing a capability from a user but one of their roles also has it, + //we have to set it to false after all or it will stay enabled. + if (actor.canHaveRoles && actor.hasCap(self.name)) { + actor.setCap(self.name, newState); + } + } + }, + owner: this, + deferEvaluation: true + }); + //this.isEnabledForSelectedActor.extend({rateLimit: {timeout: 10, method: "notifyWhenChangesStop"}}); + + this.isExplicitlyDenied = ko.pureComputed({ + read: function () { + const actor = editor.selectedActor(); + if (actor) { + return (actor.getCapabilityState(self.name) === false); + } + return false; + }, + deferEvaluation: true + }); + + this.grantedPermissions = ko.computed({ + read: () => { + const _ = wsAmeLodash; + let results = []; + + if (this.predefinedPermissions.length > 0) { + results = this.predefinedPermissions.slice(); + } + + function localeAwareCompare(a: string, b: string) { + return a.localeCompare(b); + } + + function actionsToPermissions( + actionGroups: AmeDictionary , + labelMap: AmeDictionary , + descriptions: AmeDictionary + ): string[] { + return _.map(actionGroups, (ids, action) => { + let labels = _.map(ids, (id) => labelMap[id].pluralLabel) + .sort(localeAwareCompare); + let template = descriptions[action]; + if (!template) { + template = action + ': %s'; + } + return template.replace('%s', RexCapability.formatNounList(labels)); + }).sort(localeAwareCompare); + } + + //Post permissions. + let postActionGroups = _.transform( + this.usedByPostTypeActions, + function (accumulator: { [action: string]: string[] }, actions, postType) { + let actionKeys = _.keys(actions); + + //Combine "edit" and "create" permissions because they usually use the same capability. + const editEqualsCreate = actions.hasOwnProperty('edit_posts') && actions.hasOwnProperty('create_posts'); + if (editEqualsCreate) { + actionKeys = _.without(actionKeys, 'edit_posts', 'create_posts'); + actionKeys.unshift('edit_and_create'); + } + + _.forEach(actionKeys, function (action) { + if (!accumulator.hasOwnProperty(action)) { + accumulator[action] = []; + } + accumulator[action].push(postType); + }); + }, {} + ); + + let postPermissions = actionsToPermissions( + postActionGroups, + this.editor.postTypes, + RexPostTypePermission.actionDescriptions + ); + Array.prototype.push.apply(results, postPermissions); + + //Taxonomy permissions. + let taxonomyActionGroups = _.transform( + this.usedByTaxonomyActions, + function (accumulator: { [action: string]: string[] }, actions, taxonomy) { + let actionKeys = _.keys(actions); + + //Most taxonomies use the same capability for manage_terms, edit_terms, and delete_terms. + //In those cases, let's show only manage_terms. + if (actions.hasOwnProperty('manage_terms')) { + actionKeys = _.without(actionKeys, 'edit_terms', 'delete_terms'); + } + + _.forEach(actionKeys, function (action) { + if (!accumulator.hasOwnProperty(action)) { + accumulator[action] = []; + } + accumulator[action].push(taxonomy); + }); + }, {} + ); + + let taxonomyPermissions = actionsToPermissions( + taxonomyActionGroups, + this.editor.taxonomies, + RexTaxonomyPermission.actionDescriptions + ); + Array.prototype.push.apply(results, taxonomyPermissions); + + Array.prototype.push.apply(results, this.menuItems); + return results; + }, + deferEvaluation: true, + owner: this, + }) + } + + // noinspection JSUnusedGlobalSymbols Used in KO templates. + getDocumentationUrl(): string | null { + if (this.documentationUrl) { + return this.documentationUrl; + } + if (this.originComponent && this.originComponent.capabilityDocumentationUrl) { + this.documentationUrl = this.originComponent.capabilityDocumentationUrl; + return this.documentationUrl; + } + return null; + } + + static fromJs(name: string, data: RexCapabilityData, editor: RexRoleEditor): RexCapability { + const capability = new RexCapability(name, editor); + capability.menuItems = data.menuItems.sort(function (a, b) { + return a.localeCompare(b); + }); + + if (data.componentId) { + capability.originComponent = editor.getComponent(data.componentId); + } + if (data.usedByComponents) { + for (let id in data.usedByComponents) { + const component = editor.getComponent(id); + if (component) { + capability.usedByComponents.push(component); + } + } + } + + if (data.documentationUrl) { + capability.documentationUrl = data.documentationUrl; + } + + if (data.permissions && (data.permissions.length > 0)) { + capability.predefinedPermissions = data.permissions; + } + + if ((capability.originComponent === editor.coreComponent) && (capability.documentationUrl === null)) { + capability.documentationUrl = 'https://wordpress.org/support/article/roles-and-capabilities/#' + + encodeURIComponent(capability.name); + } + + if (data.readableName) { + capability.readableName = data.readableName; + } + + return capability; + } + + static formatNounList(items: string[]): string { + if (items.length <= 2) { + return items.join(' and '); + } + return items.slice(0, -1).join(', ') + ', and ' + items[items.length - 1]; + } +} + +class RexDoNotAllowCapability extends RexCapability { + constructor(editor: RexRoleEditor) { + super('do_not_allow', editor); + this.notes = '"do_not_allow" is a special capability. ' + + 'WordPress uses it internally to indicate that access is denied. ' + + 'Normally, it should not be assigned to any roles or users.'; + + //Normally, it's impossible to grant this capability to anyone. Doing so would break things. + //However, if it's already granted, you can remove it. + this.isEditable = ko.computed(() => { + return this.isEnabledForSelectedActor(); + }); + } +} + +class RexExistCapability extends RexCapability { + constructor(editor: RexRoleEditor) { + super('exist', editor); + this.notes = '"exist" is a special capability. ' + + 'WordPress uses it internally to indicate that a role or user exists. ' + + 'Normally, everyone has this capability by default, and it is not necessary ' + + '(or possible) to assign it directly.'; + + //Everyone must have this capability. However, if it has somehow become disabled, + //we'll let the user enable it. + this.isEditable = ko.computed(() => { + return !this.isEnabledForSelectedActor(); + }); + } +} + +class RexInvalidCapability extends RexCapability { + constructor(fakeName: string, value: any, editor: RexRoleEditor) { + super(fakeName, editor); + const startsWithVowel = /^[aeiou]/i; + let theType = (typeof value); + const nounPhrase = (startsWithVowel.test(theType) ? 'an' : 'a') + ' ' + theType; + + this.notes = 'This is not a valid capability. A capability name must be a string (i.e. text),' + + ' but this is ' + nounPhrase + '. It was probably created by a bug in another plugin or theme.'; + + this.isEditable = ko.computed(() => { + return false; + }); + } +} + +class RexUserPreferences { + private readonly preferenceObservables: AmeDictionary >; + private readonly preferenceCount: KnockoutObservable ; + + private readonly plainPreferences: KnockoutComputed ; + + collapsedCategories: RexCollapsedCategorySet; + + constructor(initialPreferences?: AmeDictionary , ajaxUrl?: string, updateNonce?: string) { + const _ = wsAmeLodash; + + initialPreferences = initialPreferences || {}; + if (_.isArray(initialPreferences)) { + initialPreferences = {}; + } + + this.preferenceObservables = _.mapValues(initialPreferences, ko.observable, ko); + this.preferenceCount = ko.observable(_.size(this.preferenceObservables)); + + this.collapsedCategories = new RexCollapsedCategorySet(_.get(initialPreferences, 'collapsedCategories', [])); + + this.plainPreferences = ko.computed(() => { + //By creating a dependency on the number of preferences, we ensure that the observable will be re-evaluated + //whenever a preference is added or removed. + this.preferenceCount(); + + //This converts preferences to a plain JS object and establishes dependencies on all individual observables. + let result = _.mapValues(this.preferenceObservables, function (observable) { + return observable(); + }); + + result.collapsedCategories = this.collapsedCategories.toJs(); + + return result; + }); + + //Avoid excessive AJAX requests. + this.plainPreferences.extend({rateLimit: {timeout: 5000, method: "notifyWhenChangesStop"}}); + + //Save preferences when they change. + if (ajaxUrl && updateNonce) { + this.plainPreferences.subscribe((preferences) => { + //console.info('Saving user preferences', preferences); + jQuery.post( + ajaxUrl, + { + action: 'ws_ame_rex_update_user_preferences', + _ajax_nonce: updateNonce, + preferences: ko.toJSON(preferences) + } + ) + }); + } + } + + getObservable (name: string, defaultValue: T = null): KnockoutObservable { + if (this.preferenceObservables.hasOwnProperty(name)) { + return this.preferenceObservables[name]; + } + + const newPreference = ko.observable(defaultValue || null); + this.preferenceObservables[name] = newPreference; + this.preferenceCount(this.preferenceCount() + 1); + + return newPreference; + } +} + +/** + * An observable collection of unique strings. In this case, they're category slugs. + */ +class RexCollapsedCategorySet { + readonly items: KnockoutObservableArray ; + private isItemInSet: AmeDictionary > = {}; + + constructor(items: string[] = []) { + items = wsAmeLodash.uniq(items); + for (let i = 0; i < items.length; i++) { + this.isItemInSet[items[i]] = ko.observable(true); + } + this.items = ko.observableArray(items); + } + + private getItemObservable(item: string) { + if (!this.isItemInSet.hasOwnProperty(item)) { + this.isItemInSet[item] = ko.observable(false); + } + return this.isItemInSet[item]; + } + + add(item: string) { + if (!this.contains(item)) { + this.getItemObservable(item)(true); + this.items.push(item); + } + } + + remove(item: string) { + if (this.contains(item)) { + this.isItemInSet[item](false); + this.items.remove(item); + } + } + + toggle(item: string, addToSet: boolean) { + if (addToSet) { + this.add(item); + } else { + this.remove(item); + } + } + + contains(item: string): boolean { + return this.getItemObservable(item)(); + } + + peek(item: string): boolean { + if (!this.isItemInSet.hasOwnProperty(item)) { + return false; + } + return this.isItemInSet[item].peek(); + } + + toJs() { + return this.items(); + } +} + +interface RexCategoryData { + name: string; + componentId?: string; + slug?: string; + + capabilities?: string[]; + permissions?: { [action: string]: string }; + subcategories?: RexCategoryData[]; + + variant?: string; + contentTypeId?: string; //Post type or taxonomy name (internal ID, not display name). +} + +interface RexBaseContentData { + name: string; + label: string; + pluralLabel: string; + permissions: { [action: string]: string }; + componentId?: string; +} + +interface RexPostTypeData extends RexBaseContentData { + isDefault: boolean; +} + +interface RexTaxonomyData extends RexBaseContentData { +} + +type RexEditableRoleStrategy = 'auto' | 'none' | 'user-defined-list'; + +interface RexEditableRoleSettings { + strategy: RexEditableRoleStrategy; + userDefinedList: null | { [roleId: string]: true; }; +} + +interface RexActorEditableRoles { + [actorId: string]: RexEditableRoleSettings; +} + +interface RexAppData { + knownComponents: { [id: string]: RexComponentData }; + + capabilities: { [capabilityName: string]: RexCapabilityData }; + deprecatedCapabilities: CapabilityMap; + roles: RexRoleData[]; + users: RexUserData[]; + defaultRoleName: string; + trashedRoles: RexRoleData[]; + + coreCategory: RexCategoryData; + customCategories: RexCategoryData[]; + uncategorizedCapabilities: string[]; + userDefinedCapabilities: CapabilityMap; + + postTypes: { [postType: string]: RexPostTypeData }; + taxonomies: { [taxonomy: string]: RexTaxonomyData }; + + metaCapMap: AmeDictionary ; + + editableRoles: RexActorEditableRoles; + + selectedActor: string | null; + userPreferences: AmeDictionary ; + adminAjaxUrl: string; + updatePreferencesNonce: string; +} + +interface RexCategoryViewOption { + id: string; + label: string; +} + +class RexBaseDialog implements AmeKnockoutDialog { + isOpen: KnockoutObservable = ko.observable(false); + isRendered: KnockoutObservable = ko.observable(false); + jQueryWidget: JQuery; + title: KnockoutObservable = null; + options: AmeDictionary = { + buttons: [] + }; + + constructor() { + this.isOpen.subscribe((isOpenNow) => { + if (isOpenNow && !this.isRendered()) { + this.isRendered(true); + } + }); + } + + setupValidationTooltip(inputSelector: string, message: KnockoutObservable ) { + //Display validation messages next to the input field. + const element = this.jQueryWidget.find(inputSelector).qtip({ + overwrite: false, + content: '(Validation errors will appear here.)', + + //Show the tooltip when the input is focused. + show: { + event: '', + ready: false, + effect: false + }, + hide: { + event: '', + effect: false + }, + + position: { + my: 'center left', + at: 'center right', + effect: false + }, + style: { + classes: 'qtip-bootstrap qtip-shadow rex-tooltip' + } + }); + + message.subscribe((newMessage) => { + if (newMessage == '') { + element.qtip('option', 'content.text', 'OK'); + element.qtip('option', 'show.event', ''); + element.qtip('hide'); + } else { + element.qtip('option', 'content.text', newMessage); + element.qtip('option', 'show.event', 'focus'); + element.qtip('show'); + } + }); + + //Hide the tooltip when the dialog is closed and prevent it from automatically re-appearing. + this.isOpen.subscribe((isDialogOpen) => { + if (!isDialogOpen) { + element.qtip('option', 'show.event', ''); + element.qtip('hide'); + } + }); + }; +} + +interface RexDeletableCapItem { + capability: RexCapability; + isSelected: KnockoutObservable ; +} + +class RexDeleteCapDialog extends RexBaseDialog { + options = { + buttons: [], + minWidth: 380 + }; + + deletableItems: KnockoutComputed ; + selectedItemCount: KnockoutComputed ; + isDeleteButtonEnabled: KnockoutComputed ; + + private wasEverOpen: KnockoutObservable = ko.observable(false); + + constructor(editor: RexRoleEditor) { + super(); + const _ = wsAmeLodash; + + this.options.buttons.push({ + text: 'Delete Capability', + 'class': 'button button-primary rex-delete-selected-caps', + click: () => { + let selectedCapabilities = _.chain(this.deletableItems()) + .filter(function (item) { + return item.isSelected(); + }) + .pluck ('capability') + .value(); + + //Note: We could remove confirmation if we get an "Undo" feature. + const noun = (selectedCapabilities.length === 1) ? 'capability' : 'capabilities'; + const warning = 'Caution: Deleting capabilities could break plugins that use those capabilities. ' + + 'Delete ' + selectedCapabilities.length + ' ' + noun + '?'; + if (!confirm(warning)) { + return; + } + + this.isOpen(false); + + editor.deleteCapabilities(selectedCapabilities); + + alert(selectedCapabilities.length + ' capabilities deleted'); + }, + disabled: true + }); + + this.isOpen.subscribe((open) => { + if (open && !this.wasEverOpen()) { + this.wasEverOpen(true); + } + }); + + this.deletableItems = ko.pureComputed({ + read: () => { + const wpCore = editor.getComponent(':wordpress:'); + return _.chain(editor.capabilities) + .filter(function (capability) { + if (capability.originComponent === wpCore) { + return false; + } + return !capability.isDeleted(); + }) + //Pre-populate part of the list when the dialog is closed to ensure it has a non-zero height. + .take(this.wasEverOpen() ? 1000000 : 30) + .sortBy(function (capability) { + return capability.name.toLowerCase(); + }) + .map(function (capability) { + return { + 'capability': capability, + 'isSelected': ko.observable(false) + }; + }) + .value(); + }, + deferEvaluation: true + }); + + this.selectedItemCount = ko.pureComputed({ + read: () => _.filter(this.deletableItems(), function (item) { + return item.isSelected(); + }).length, + deferEvaluation: true + }); + + const deleteButtonText = ko.pureComputed({ + read: () => { + const count = this.selectedItemCount(); + if (count <= 0) { + return 'Delete Capability'; + } else { + if (count === 1) { + return 'Delete 1 Capability'; + } else { + return ('Delete ' + count + ' Capabilities'); + } + } + }, + deferEvaluation: true + }); + + deleteButtonText.subscribe((newText) => { + this.jQueryWidget + .closest('.ui-dialog') + .find('.ui-dialog-buttonset .button-primary .ui-button-text') + .text(newText); + }); + + this.isDeleteButtonEnabled = ko.pureComputed({ + read: () => { + return this.selectedItemCount() > 0; + }, + deferEvaluation: true + }) + } + + onOpen() { + //Deselect all items when the dialog is opened. + const items = this.deletableItems(); + for (let i = 0; i < items.length; i++) { + if (items[i].isSelected()) { + items[i].isSelected(false); + } + } + } +} + +class RexAddCapabilityDialog extends RexBaseDialog { + public static readonly states = { + valid: 'valid', + empty: 'empty', + notice: 'notice', + error: 'error' + }; + + autoCancelButton: boolean = true; + options: AmeDictionary = { + minWidth: 380 + }; + + capabilityName: KnockoutComputed ; + validationState: KnockoutObservable = ko.observable(RexAddCapabilityDialog.states.empty); + validationMessage: KnockoutObservable = ko.observable(''); + isAddButtonEnabled: KnockoutComputed ; + + private readonly editor: RexRoleEditor; + + constructor(editor: RexRoleEditor) { + super(); + const _ = wsAmeLodash; + this.editor = editor; + + const excludedCaps = ['do_not_allow', 'exist', 'customize']; + + let newCapabilityName = ko.observable(''); + this.capabilityName = ko.computed({ + read: function () { + return newCapabilityName(); + }, + write: (value) => { + value = _.trimRight(value); + + //Validate and sanitize the capability name. + let state = this.validationState, + message = this.validationMessage; + + //WP API allows completely arbitrary capability names, but this plugin forbids some characters + //for sanity's sake and to avoid XSS. + const invalidCharacters = /[><&\r\n\t]/g; + //While all other characters are allowed, it's recommended to stick to alphanumerics, + //underscores and dashes. Spaces are also OK because some other plugins use them. + const suspiciousCharacters = /[^a-z0-9_ -]/ig; + //PHP doesn't allow numeric string keys, and there's no conceivable reason to start the name with a space. + const invalidFirstCharacter = /^[\s0-9]/i; + + let foundInvalid = value.match(invalidCharacters); + let foundSuspicious = value.match(suspiciousCharacters); + + if (foundInvalid !== null) { + state(RexAddCapabilityDialog.states.error); + message('Sorry, ' + _.escape(_.last(foundInvalid)) + '
is not allowed here.'); + + } else if (value.match(invalidFirstCharacter) !== null) { + state(RexAddCapabilityDialog.states.error); + message('Capability name should start with a letter or an underscore.'); + + } else if (editor.capabilityExists(value)) { + //Duplicates are not allowed. + state(RexAddCapabilityDialog.states.error); + message('That capability already exists.'); + + } else if (editor.getRole(value) !== null) { + state(RexAddCapabilityDialog.states.error); + message('Capability name can\'t be the same as the name of a role.'); + + } else if (excludedCaps.indexOf(value) >= 0) { + state(RexAddCapabilityDialog.states.error); + message('That is a meta capability or a reserved capability name.'); + + } else if (foundSuspicious !== null) { + state(RexAddCapabilityDialog.states.notice); + message('For best compatibility, we recommend using only English letters, numbers, and underscores.'); + + } else if (value === '') { + //Empty input, nothing to validate. + state(RexAddCapabilityDialog.states.empty); + message(''); + + } else { + state(RexAddCapabilityDialog.states.valid); + message(''); + } + + newCapabilityName(value); + } + }); + + const acceptableStates = [RexAddCapabilityDialog.states.valid, RexAddCapabilityDialog.states.notice]; + this.isAddButtonEnabled = ko.pureComputed(() => { + return (acceptableStates.indexOf(this.validationState()) >= 0); + }); + + this.options.buttons = [{ + text: 'Add Capability', + 'class': 'button button-primary', + click: () => { + this.onConfirm(); + }, + disabled: true + }]; + } + + onOpen(event, ui) { + //Clear the input when the dialog is opened. + this.capabilityName(''); + } + + onConfirm() { + if (!this.isAddButtonEnabled()) { + return; + } + const category = this.editor.addCapability(this.capabilityName().trim()); + this.isOpen(false); + + //Note: Maybe the user doesn't need this alert? Hmm. + if (!category || (this.editor.categoryViewMode() === RexRoleEditor.listView)) { + alert('Capability added'); + } else { + alert('Capability added to the "' + category.getAbsoluteName() + '" category.'); + } + } +} + +class RexAddRoleDialog extends RexBaseDialog { + roleName: KnockoutObservable= ko.observable(''); + roleDisplayName: KnockoutObservable = ko.observable(''); + roleToCopyFrom: KnockoutObservable = ko.observable(null); + + isAddButtonEnabled: KnockoutComputed ; + + isNameValid: KnockoutComputed ; + nameValidationMessage: KnockoutObservable = ko.observable(''); + isDisplayNameValid: KnockoutComputed ; + displayNameValidationMessage: KnockoutObservable = ko.observable(''); + + private readonly editor: RexRoleEditor; + private areTooltipsInitialised: boolean = false; + + private static readonly invalidDisplayNameRegex = /[><&\r\n\t]/; + + constructor(editor: RexRoleEditor) { + super(); + const _ = wsAmeLodash; + this.editor = editor; + + this.options.minWidth = 380; + this.options.buttons.push({ + text: 'Add Role', + 'class': 'button button-primary', + click: this.onConfirm.bind(this), + disabled: true + }); + + this.roleDisplayName.extend({rateLimit: 10}); + this.roleName.extend({rateLimit: 10}); + + //Role names are restricted - you can only use lowercase Latin letters, numbers and underscores. + const roleNameCharacterGroup = 'a-z0-9_'; + const invalidCharacterRegex = new RegExp('[^' + roleNameCharacterGroup + ']', 'g'); + const numbersOnlyRegex = /^[0-9]+$/; + + this.isNameValid = ko.computed(() => { + let name = this.roleName().trim(); + let message = this.nameValidationMessage; + + //Name must not be empty. + if (name === '') { + message(''); + return false; + } + + //Name can only contain certain characters. + const invalidChars = name.match(invalidCharacterRegex); + if (invalidChars !== null) { + let lastInvalidChar = _.last(invalidChars); + if (lastInvalidChar === ' ') { + lastInvalidChar = 'space'; + } + message( + 'Sorry, ' + _.escape(lastInvalidChar) + '
is not allowed here.
' + + 'Please enter only lowercase English letters, numbers, and underscores.' + ); + return false; + } + + //Numeric names could cause problems with how PHP handles associative arrays. + if (numbersOnlyRegex.test(name)) { + message('Numeric names are not allowed. Please add at least one letter or underscore.'); + return false; + } + + //Name must not be a duplicate. + let existingRole = editor.getRole(name); + if (existingRole) { + message('Duplicate role name.'); + return false; + } + + //WP stores capabilities and role names in the same associative array, + //so they must be unique with respect to each other. + if (editor.capabilityExists(name)) { + message('Role name can\'t be the same as a capability name.'); + return false; + } + + message(''); + return true; + }); + + this.isDisplayNameValid = ko.computed(() => { + let name = this.roleDisplayName(); + let message = this.displayNameValidationMessage; + return RexAddRoleDialog.validateDisplayName(name, message); + }); + + //Automatically generate a role name from the display name. Basically, turn it into a slug. + let lastAutoRoleName = null; + this.roleDisplayName.subscribe((displayName) => { + let slug = _.snakeCase(displayName); + + //Use the auto-generated role name only if the user hasn't entered their own. + let currentValue = this.roleName(); + if ((currentValue === '') || (currentValue === lastAutoRoleName)) { + this.roleName(slug); + } + lastAutoRoleName = slug; + }); + + this.isAddButtonEnabled = ko.pureComputed({ + read: () => { + return (this.roleName() !== '') && (this.roleDisplayName() !== '') + && this.isNameValid() && this.isDisplayNameValid(); + }, + deferEvaluation: true + }); + } + + static validateDisplayName(name: string, validationMessage: KnockoutObservable): boolean { + name = name.trim(); + + if (name === '') { + validationMessage(''); + return false; + } + + //You can choose pretty much any display name you like, but we'll forbid special characters + //that might cause problems for plugins that don't escape output for HTML. + if (RexAddRoleDialog.invalidDisplayNameRegex.test(name)) { + validationMessage('Sorry, these characters are not allowed: < > &
'); + return false; + } + + validationMessage(''); + return true; + } + + onOpen(event, ui) { + //Clear dialog fields when it's opened. + this.roleName(''); + this.roleDisplayName(''); + this.roleToCopyFrom(null); + + if (!this.areTooltipsInitialised) { + this.setupValidationTooltip('#rex-new-role-display-name', this.displayNameValidationMessage); + this.setupValidationTooltip('#rex-new-role-name', this.nameValidationMessage); + this.areTooltipsInitialised = true; + } + } + + onConfirm() { + if (!this.isAddButtonEnabled()) { + return; + } + + this.isOpen(false); + + let caps = {}; + if (this.roleToCopyFrom()) { + caps = this.roleToCopyFrom().getOwnCapabilities(); + } + + this.editor.addRole(this.roleName(), this.roleDisplayName(), caps); + } +} + +class RexDeleteRoleDialog extends RexBaseDialog { + isDeleteButtonEnabled: KnockoutComputed; + + private editor: RexRoleEditor; + private isRoleSelected: AmeDictionary > = {}; + + constructor(editor: RexRoleEditor) { + super(); + this.editor = editor; + + this.options.minWidth = 420; + this.options.buttons.push({ + text: 'Delete Role', + 'class': 'button button-primary', + click: this.onConfirm.bind(this), + disabled: true + }); + + this.isDeleteButtonEnabled = ko.pureComputed({ + read: () => { + return this.getSelectedRoles().length > 0; + }, + deferEvaluation: true + }); + } + + onConfirm() { + const _ = wsAmeLodash; + let rolesToDelete = this.getSelectedRoles(); + + //Warn about the dangers of deleting built-in roles. + let selectedBuiltInRoles = _.filter(rolesToDelete, _.method('isBuiltIn')); + if (selectedBuiltInRoles.length > 0) { + const warning = 'Caution: Deleting default roles like ' + _.first(selectedBuiltInRoles).displayName() + + ' can prevent you from using certain plugins. This is because some plugins look for specific' + + ' role names to determine if a user is allowed to access the plugin.' + + '\nDelete ' + selectedBuiltInRoles.length + ' default role(s)?'; + if (!confirm(warning)) { + return; + } + } + this.editor.deleteRoles(rolesToDelete); + this.isOpen(false); + } + + onOpen(event, ui) { + //Deselect all previously selected roles. + wsAmeLodash.forEach(this.isRoleSelected, function (isSelected) { + isSelected(false); + }); + } + + getSelectionState(roleName: string): KnockoutObservable { + if (!this.isRoleSelected.hasOwnProperty(roleName)) { + this.isRoleSelected[roleName] = ko.observable(false); + } + return this.isRoleSelected[roleName]; + } + + private getSelectedRoles(): RexRole[] { + const _ = wsAmeLodash; + let rolesToDelete = []; + _.forEach(this.editor.roles(), (role) => { + if (this.getSelectionState(role.name())()) { + rolesToDelete.push(role); + } + }); + return rolesToDelete; + } +} + +class RexRenameRoleDialog extends RexBaseDialog { + private editor: RexRoleEditor; + isConfirmButtonEnabled: KnockoutComputed ; + selectedRole: KnockoutObservable = ko.observable(null); + + newDisplayName: KnockoutObservable = ko.observable(''); + displayNameValidationMessage: KnockoutObservable = ko.observable(''); + isTooltipInitialised: boolean = false; + + constructor(editor: RexRoleEditor) { + super(); + this.editor = editor; + + this.options.minWidth = 380; + this.options.buttons.push({ + text: 'Rename Role', + 'class': 'button button-primary', + click: this.onConfirm.bind(this), + disabled: true + }); + + this.selectedRole.subscribe((role) => { + if (role) { + this.newDisplayName(role.displayName()); + } + }); + + this.isConfirmButtonEnabled = ko.computed({ + read: () => { + return RexAddRoleDialog.validateDisplayName(this.newDisplayName(), this.displayNameValidationMessage); + }, + deferEvaluation: true + }); + } + + onOpen(event, ui) { + const _ = wsAmeLodash; + + if (!this.isTooltipInitialised) { + this.setupValidationTooltip('#rex-edited-role-display-name', this.displayNameValidationMessage); + this.isTooltipInitialised = true; + } + + //Select either the currently selected role or the first available role. + const selectedActor = this.editor.selectedActor(); + if (selectedActor && (selectedActor instanceof RexRole)) { + this.selectedRole(selectedActor); + } else { + this.selectedRole(_.first(this.editor.roles())); + } + } + + onConfirm() { + if (!this.isConfirmButtonEnabled()) { + return; + } + if (this.selectedRole()) { + const name = this.newDisplayName().trim(); + this.selectedRole().displayName(name); + this.editor.actorSelector.repopulate(); + } + this.isOpen(false); + } +} + +class RexEagerObservableStringSet { + private items: Record > = {}; + + public contains(item: string): boolean { + if (!this.items.hasOwnProperty(item)) { + this.items[item] = ko.observable(false); + return false; + } + return this.items[item](); + } + + public add(item: string) { + if (!this.items.hasOwnProperty(item)) { + this.items[item] = ko.observable(true); + } else { + this.items[item](true); + } + } + + public remove(item: string) { + if (this.items.hasOwnProperty(item)) { + this.items[item](false); + } + } + + public clear() { + const _ = wsAmeLodash; + _.forEach(this.items, (isInSet) => { + isInSet(false); + }); + } + + public getPresenceObservable(item: string): KnockoutObservable { + if (!this.items.hasOwnProperty(item)) { + this.items[item] = ko.observable(false); + } + return this.items[item]; + } + + public getAsObject (fillValue: T | boolean = true): Record { + const _ = wsAmeLodash; + let output = {}; + _.forEach(this.items, (isInSet, item) => { + if (isInSet()) { + output[item] = fillValue; + } + }); + return output; + } +} + +class RexObservableEditableRoleSettings { + strategy: KnockoutObservable ; + userDefinedList: RexEagerObservableStringSet; + + constructor() { + this.strategy = ko.observable('auto'); + this.userDefinedList = new RexEagerObservableStringSet(); + } + + toPlainObject(): RexEditableRoleSettings { + let roleList = this.userDefinedList.getAsObject (true); + if (wsAmeLodash.isEmpty(roleList)) { + roleList = null; + } + return { + strategy: this.strategy(), + userDefinedList: roleList + }; + } +} + +class RexUserRoleModule { + primaryRole: KnockoutObservable ; + private roleObservables: { + [roleId: string]: { + role: RexRole; + selectedActorHasRole: KnockoutComputed ; + } + } = {}; + + private readonly selectedActor: KnockoutObservable ; + public readonly sortedRoles: KnockoutObservable ; + + public readonly isVisible: KnockoutObservable ; + + constructor(selectedActor: KnockoutObservable , roles: KnockoutObservableArray ) { + this.selectedActor = selectedActor; + this.sortedRoles = ko.computed(() => { + return roles(); + }); + this.primaryRole = ko.computed({ + read: () => { + const actor = selectedActor(); + if ((actor === null) || !actor.canHaveRoles) { + return null; + } + if (actor instanceof RexUser) { + const roles = actor.roles(); + if (roles.length < 1) { + return null; + } + return roles[0]; + } + return null; + }, + write: (newRole: RexRole | null) => { + const actor = selectedActor(); + if ((actor === null) || !actor.canHaveRoles || !(actor instanceof RexUser)) { + return; + } + + //No primary role = no roles at all. + if (newRole === null) { + actor.roles.removeAll(); + return; + } + + //Sanity check. + if (!(newRole instanceof RexRole)) { + return; + } + + if (!this.canAssignRoleToActor(newRole)) { + return; + } + + //Remove the previous primary role. + const oldPrimaryRole = (actor.roles().length > 0) ? actor.roles()[0] : null; + if (oldPrimaryRole !== null) { + actor.roles.remove(oldPrimaryRole); + } + + //If the user already has the new role, remove it from its old position first. + if (actor.roles.indexOf(newRole) !== -1) { + actor.roles.remove(newRole); + } + //Add the role to the top of the list. + actor.roles.unshift(newRole); + } + }); + + this.isVisible = ko.pureComputed(() => { + const actor = this.selectedActor(); + return (actor !== null) && actor.canHaveRoles; + }); + } + + // noinspection JSUnusedGlobalSymbols Used in Knockout templates. + actorHasRole(role: RexRole): KnockoutObservable { + const roleActorId = role.getId(); + if (this.roleObservables.hasOwnProperty(roleActorId) && (this.roleObservables[roleActorId].role === role)) { + return this.roleObservables[roleActorId].selectedActorHasRole; + } + + let selectedActorHasRole = ko.computed ({ + read: () => { + const actor = this.selectedActor(); + if ((actor === null) || !actor.canHaveRoles) { + return false; + } + if (actor instanceof RexUser) { + return (actor.roles.indexOf(role) !== -1); + } + return false; + }, + write: (shouldHaveRole) => { + const actor = this.selectedActor(); + if ((actor === null) || !actor.canHaveRoles || !(actor instanceof RexUser)) { + return; + } + if (!this.canAssignRoleToActor(role)) { + return; + } + + const alreadyHasRole = (actor.roles.indexOf(role) !== -1); + if (shouldHaveRole !== alreadyHasRole) { + if (shouldHaveRole) { + actor.roles.push(role); + } else { + actor.roles.remove(role); + } + } + } + }); + + this.roleObservables[roleActorId] = { + role: role, + selectedActorHasRole: selectedActorHasRole + }; + + return selectedActorHasRole; + } + + canAssignRoleToActor(role: RexRole): boolean { + //This is a stub. The role editor currently doesn't check editable role settings at edit time. + const actor = this.selectedActor(); + if ((actor === null) || !actor.canHaveRoles) { + return false; + } + return (role instanceof RexRole); + } +} + +class RexEditableRolesDialog extends RexBaseDialog { + private editor: RexRoleEditor; + private readonly visibleActors: KnockoutObservableArray ; + selectedActor: KnockoutObservable = ko.observable(null); + + private actorSettings: { [actorId: string]: RexObservableEditableRoleSettings; } = {}; + private selectedActorSettings: KnockoutObservable ; + + editableRoleStrategy: KnockoutComputed ; + isAutoStrategyAllowed: KnockoutComputed ; + isListStrategyAllowed: KnockoutComputed ; + + constructor(editor: RexRoleEditor) { + super(); + this.editor = editor; + this.visibleActors = ko.observableArray([]); + + this.options.minWidth = 600; + this.options.buttons.push({ + text: 'Save Changes', + 'class': 'button button-primary', + click: this.onConfirm.bind(this), + disabled: false + }); + + //Super Admin is always set to "leave unchanged" because + //they can edit all roles. + const superAdmin = editor.getSuperAdmin(); + const superAdminSettings = new RexObservableEditableRoleSettings(); + superAdminSettings.strategy('none'); + + const dummySettings = new RexObservableEditableRoleSettings(); + this.selectedActorSettings = ko.computed(() => { + if (this.selectedActor() === null) { + return dummySettings; + } + if (this.selectedActor() === superAdmin) { + return superAdminSettings; + } + const actorId = this.selectedActor().getId(); + if (!this.actorSettings.hasOwnProperty(actorId)) { + //This should never happen; the dictionary should be initialised when opening the dialog. + this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); + } + return this.actorSettings[actorId]; + }); + + this.editableRoleStrategy = ko.computed({ + read: () => { + return this.selectedActorSettings().strategy(); + }, + write: (newValue: string) => { + this.selectedActorSettings().strategy(newValue as RexEditableRoleStrategy); + } + }); + + this.isAutoStrategyAllowed = ko.computed(() => { + const actor = this.selectedActor(); + if (actor == null) { + return true; + } + return !( + (actor === superAdmin) + || ((actor instanceof RexUser) && actor.isSuperAdmin) + ); + }); + this.isListStrategyAllowed = this.isAutoStrategyAllowed; + } + + onOpen(event, ui) { + const _ = wsAmeLodash; + + //Copy editable role settings into observables. + _.forEach(this.editor.actorEditableRoles, (settings: RexEditableRoleSettings, actorId: string) => { + if (!this.actorSettings.hasOwnProperty(actorId)) { + this.actorSettings[actorId] = new RexObservableEditableRoleSettings(); + } + const observableSettings = this.actorSettings[actorId]; + observableSettings.strategy(settings.strategy); + observableSettings.userDefinedList.clear(); + if (settings.userDefinedList !== null) { + _.forEach(settings.userDefinedList, (ignored, roleId: string) => { + observableSettings.userDefinedList.add(roleId); + }); + } + }); + + this.visibleActors(this.editor.actorSelector.getVisibleActors()); + + //Select either the currently selected actor or the first role. + const selectedActor = this.editor.selectedActor(); + if (selectedActor) { + this.selectedActor(selectedActor); + } else { + this.selectedActor(_.first(this.editor.roles())); + } + } + + onConfirm() { + //Save editable roles + const _ = wsAmeLodash; + let settings = this.editor.actorEditableRoles; + _.forEach(this.actorSettings, (observableSettings, actorId) => { + if (observableSettings.strategy() === 'auto') { + //"auto" is the default so we don't need to store anything. + delete settings[actorId]; + } else { + settings[actorId] = observableSettings.toPlainObject(); + } + }); + + this.isOpen(false); + } + + isRoleSetToEditable(role: RexBaseActor) { + return this.selectedActorSettings().userDefinedList.getPresenceObservable(role.name()); + } + + isRoleEnabled(role: RexBaseActor) { + return this.editableRoleStrategy() === 'user-defined-list'; + } + + selectItem(actor: RexBaseActor) { + this.selectedActor(actor); + } + + getItemText(actor: RexBaseActor): string { + return this.editor.actorSelector.getNiceName(actor); + } +} + +class RexRoleEditor implements AmeActorManagerInterface { + public static readonly hierarchyView = { + label: 'Hierarchy view', + id: 'hierarchy', + templateName: 'rex-hierarchy-view-template' + }; + public static readonly singleCategoryView = { + label: 'Category view', + id: 'category', + templateName: 'rex-single-category-view-template' + }; + public static readonly listView = {label: 'List view', id: 'list', templateName: 'rex-list-view-template'}; + // noinspection JSUnusedGlobalSymbols + readonly categoryViewOptions: RexCategoryViewOption[] = [ + RexRoleEditor.hierarchyView, + RexRoleEditor.singleCategoryView, + RexRoleEditor.listView + ]; + + readonly selectedActor: KnockoutComputed ; + readonly readableNamesEnabled: KnockoutObservable ; + + roles: KnockoutObservableArray ; + users: KnockoutObservableArray ; + capabilities: { [capability: string]: RexCapability }; + + postTypes: { [name: string]: RexPostTypeData }; + taxonomies: { [name: string]: RexTaxonomyData }; + + private deprecatedCapabilities: CapabilityMap = {}; + private readonly metaCapabilityMap: AmeDictionary ; + + private readonly userDefinedCapabilities: CapabilityMap = {}; + + private readonly components: { [id: string]: RexWordPressComponent }; + readonly coreComponent: RexWordPressComponent; + + rootCategory: RexCategory; + selectedCategory: KnockoutComputed ; + categoriesBySlug: AmeDictionary = {}; + categoryViewMode: KnockoutObservable ; + capabilityViewClasses: KnockoutObservable ; + readonly leafCategories: KnockoutComputed ; + allCapabilitiesAsPermissions: KnockoutComputed ; + /** + * Index of capabilities that are in the selected category or its subcategories. + */ + private readonly capsInSelectedCategory: KnockoutComputed >; + + private readonly defaultNewUserRoleName: string; + defaultRoles: KnockoutComputed ; + customRoles: KnockoutComputed ; + trashedRoles: KnockoutObservableArray ; + + actorSelector: AmeActorSelector; + private actorLookup: AmeDictionary = {}; + private readonly dummyActor: RexRole; + permissionTipSubject: KnockoutObservable ; + + searchQuery: KnockoutObservable ; + searchKeywords: KnockoutComputed ; + + public readonly userPreferences: RexUserPreferences; + + public actorEditableRoles: RexActorEditableRoles; + + public readonly isLoaded: KnockoutObservable ; + public readonly areBindingsApplied: KnockoutObservable ; + + /** + * Show deprecated capabilities. + */ + showDeprecatedEnabled: KnockoutObservable ; + /** + * Show CPT or taxonomy permissions that use the same capability as another permission on that CPT/taxonomy. + */ + showRedundantEnabled: KnockoutObservable ; + /** + * Show CPT or taxonomy capabilities that match the built-in "Posts" post type or the "Categories" taxonomy. + */ + showBaseCapsEnabled: KnockoutObservable ; + + /** + * Show only checked (granted) capabilities. + */ + showOnlyCheckedEnabled: KnockoutObservable ; + + showNumberOfCapsEnabled: KnockoutObservable ; + showTotalCapCountEnabled: KnockoutObservable ; + showGrantedCapCountEnabled: KnockoutObservable ; + showZerosEnabled: KnockoutObservable ; + + categoryWidthMode: KnockoutObservable ; + + inheritanceOverrideEnabled: KnockoutObservable ; + + deleteCapabilityDialog: AmeKnockoutDialog; + addCapabilityDialog: RexAddCapabilityDialog; + addRoleDialog: AmeKnockoutDialog; + deleteRoleDialog: AmeKnockoutDialog; + renameRoleDialog: AmeKnockoutDialog; + editableRolesDialog: AmeKnockoutDialog; + + userRoleModule: RexUserRoleModule; + + settingsFieldData: KnockoutObservable ; + isSaving: KnockoutObservable ; + isGlobalSettingsUpdate: KnockoutObservable ; + + public readonly isShiftKeyDown: KnockoutObservable ; + + constructor(data: RexAppData) { + const self = this; + const _ = wsAmeLodash; + + this.areBindingsApplied = ko.observable(false); + this.isLoaded = ko.computed(() => { + return this.areBindingsApplied(); + }); + + this.userPreferences = new RexUserPreferences(data.userPreferences, data.adminAjaxUrl, data.updatePreferencesNonce); + + const preferences = this.userPreferences; + this.showDeprecatedEnabled = preferences.getObservable('showDeprecatedEnabled', true); + this.showRedundantEnabled = preferences.getObservable('showRedundantEnabled', false); + this.showBaseCapsEnabled = ko.computed (this.showRedundantEnabled); + this.showOnlyCheckedEnabled = preferences.getObservable('showOnlyCheckedEnabled', false); + this.categoryWidthMode = preferences.getObservable ('categoryWidthMode', 'adaptive'); + + this.readableNamesEnabled = preferences.getObservable ('readableNamesEnabled', true); + + this.showNumberOfCapsEnabled = preferences.getObservable('showNumberOfCapsEnabled', true); + this.showGrantedCapCountEnabled = preferences.getObservable('showGrantedCapCountEnabled', true); + this.showTotalCapCountEnabled = preferences.getObservable('showTotalCapCountEnabled', true); + this.showZerosEnabled = preferences.getObservable('showZerosEnabled', false); + this.inheritanceOverrideEnabled = preferences.getObservable('inheritanceOverrideEnabled', false); + + //Remember and restore the selected view mode. + let viewModeId = preferences.getObservable('categoryVewMode', 'hierarchy'); + let initialViewMode = _.find(this.categoryViewOptions, 'id', viewModeId()); + if (!initialViewMode) { + initialViewMode = RexRoleEditor.hierarchyView; + } + this.categoryViewMode = ko.observable(initialViewMode); + this.categoryViewMode.subscribe(function (newMode) { + viewModeId(newMode.id); + }); + + this.isShiftKeyDown = ko.observable(false); + + this.capabilityViewClasses = ko.pureComputed({ + read: () => { + const viewMode = this.categoryViewMode(); + let classes = ['rex-category-view-mode-' + viewMode.id]; + if (viewMode === RexRoleEditor.singleCategoryView) { + classes.push('rex-show-category-subheadings'); + } + if (this.readableNamesEnabled()) { + classes.push('rex-readable-names-enabled'); + } + if (this.categoryWidthMode() === 'full') { + classes.push('rex-full-width-categories'); + } + return classes.join(' '); + }, + deferEvaluation: true + }); + + this.searchQuery = ko.observable('').extend({rateLimit: {timeout: 100, method: "notifyWhenChangesStop"}}); + this.searchKeywords = ko.computed(function () { + let query = self.searchQuery().trim(); + if (query === '') { + return []; + } + + return wsAmeLodash(query.split(' ')) + .map((keyword) => { + return keyword.trim() + }) + .filter((keyword) => { + return (keyword !== ''); + }) + .value(); + }); + + this.components = _.mapValues(data.knownComponents, (details, id) => { + return RexWordPressComponent.fromJs(id, details); + }); + this.coreComponent = new RexWordPressComponent(':wordpress:', 'WordPress core'); + this.components[':wordpress:'] = this.coreComponent; + + //Populate roles and users. + const tempRoleList = []; + _.forEach(data.roles, (roleData) => { + const role = new RexRole(roleData.name, roleData.displayName, roleData.capabilities); + role.hasUsers = roleData.hasUsers; + tempRoleList.push(role); + this.actorLookup[role.id()] = role; + }); + this.roles = ko.observableArray(tempRoleList); + + const tempUserList = []; + _.forEach(AmeActors.getUsers(), (data) => { + const user = RexUser.fromAmeUser(data, self); + tempUserList.push(user); + this.actorLookup[user.id()] = user; + }); + this.users = ko.observableArray(tempUserList); + + this.dummyActor = new RexRole('rex-invalid-role', 'Invalid Role'); + this.defaultNewUserRoleName = data.defaultRoleName; + this.trashedRoles = ko.observableArray(_.map(data.trashedRoles, function (roleData) { + return RexRole.fromRoleData(roleData); + })); + + this.actorSelector = new AmeActorSelector(this, true, false); + //Wrap the selected actor in a computed observable so that it can be used with Knockout. + let _selectedActor = ko.observable(this.getActor(this.actorSelector.selectedActor)); + this.selectedActor = ko.computed({ + read: function () { + return _selectedActor(); + }, + write: (newActor: RexBaseActor) => { + this.actorSelector.setSelectedActor(newActor.id()); + } + }); + this.actorSelector.onChange((newSelectedActor: string) => { + _selectedActor(this.getActor(newSelectedActor)); + }); + + //Refresh the actor selector when roles are added or removed. + this.roles.subscribe(() => { + this.actorSelector.repopulate(); + }); + + //Re-select the previously selected actor if possible. + let initialActor: RexBaseActor = null; + if (data.selectedActor) { + initialActor = this.getActor(data.selectedActor); + } + if (!initialActor || (initialActor === this.dummyActor)) { + initialActor = this.roles()[0]; + } + this.selectedActor(initialActor); + + //Populate capabilities. + this.deprecatedCapabilities = data.deprecatedCapabilities; + this.metaCapabilityMap = data.metaCapMap; + this.userDefinedCapabilities = data.userDefinedCapabilities; + + this.capabilities = _.mapValues(data.capabilities, (metadata: RexCapabilityData, name: string) => { + return RexCapability.fromJs(name, metadata, self); + }); + + //Add the special "do_not_allow" capability. Normally, it's impossible to assign it to anyone, + //but it can still be used in post type permissions and other places. + const doNotAllow = new RexDoNotAllowCapability(this); + doNotAllow.originComponent = this.components[':wordpress:']; + this.capabilities['do_not_allow'] = doNotAllow; + + //Similarly, "exist" is always enabled for all roles and users. Everyone can exist. + if (this.capabilities.hasOwnProperty('exist')) { + this.capabilities['exist'] = new RexExistCapability(this); + this.capabilities['exist'].originComponent = this.components[':wordpress:']; + } + + //Store editable roles. + this.actorEditableRoles = (!_.isEmpty(data.editableRoles)) ? data.editableRoles : {}; + + this.rootCategory = new RexCategory('All', this); + + const coreCategory = RexCategory.fromJs(data.coreCategory, this); + this.rootCategory.addSubcategory(coreCategory); + + const postTypeCategory = new RexPostTypeContainerCategory('Post Types', this, 'postTypes'); + this.postTypes = _.indexBy(data.postTypes, 'name'); + _.forEach(this.postTypes, (details: RexPostTypeData, id) => { + const category = new RexPostTypeCategory(details.label, self, id, 'postTypes/' + id, details.permissions, details.isDefault); + if (details.componentId) { + category.origin = this.getComponent(details.componentId); + } + postTypeCategory.addSubcategory(category); + + //Record the post type actions associated with each capability. + for (let action in details.permissions) { + const capability = self.getCapability(details.permissions[action]); + _.set(capability.usedByPostTypeActions, [details.name, action], true); + } + }); + //Sort the actual subcategory array. + postTypeCategory.sortSubcategories(); + this.rootCategory.addSubcategory(postTypeCategory); + + //Taxonomies. + this.taxonomies = data.taxonomies; + const taxonomyCategory = new RexTaxonomyContainerCategory('Taxonomies', this, 'taxonomies'); + _.forEach(data.taxonomies, (details, id) => { + const category = new RexTaxonomyCategory(details.label, self, id, 'taxonomies/' + id, details.permissions); + taxonomyCategory.addSubcategory(category); + + //Record taxonomy type actions associated with each capability. + for (let action in details.permissions) { + const capability = self.getCapability(details.permissions[action]); + _.set(capability.usedByTaxonomyActions, [details.name, action], true); + } + }); + taxonomyCategory.subcategories.sort((a, b) => { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + + this.rootCategory.addSubcategory(taxonomyCategory); + + const customParentCategory = new RexCategory('Plugins', this, 'custom'); + + function initCustomCategory(details: RexCategoryData, parent: RexCategory) { + let category = RexCategory.fromJs(details, self); + + //Sort subcategories by title. + category.subcategories.sort((a, b) => { + //Keep the "General" category at the top if there is one. + if (a.name === b.name) { + return 0 + } else if (a.name === 'General') { + return -1; + } else if (b.name === 'General') { + return 1; + } + return a.name.localeCompare(b.name); + }); + + parent.addSubcategory(category); + } + + _.forEach(data.customCategories, (details) => { + initCustomCategory(details, customParentCategory); + }); + customParentCategory.subcategories.sort((a, b) => { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + this.rootCategory.addSubcategory(customParentCategory); + + //Make a category for uncategorized capabilities. This one is always at the bottom. + const uncategorizedCategory = new RexCategory( + 'Uncategorized', + self, + 'custom/uncategorized', + data.uncategorizedCapabilities + ); + customParentCategory.addSubcategory(uncategorizedCategory); + + let _selectedCategory: KnockoutObservable = ko.observable(null); + this.selectedCategory = ko.computed({ + read: function () { + return _selectedCategory(); + }, + write: function (newSelection: RexCategory) { + const oldSelection = _selectedCategory(); + if (newSelection === oldSelection) { + return; + } + + if (newSelection) { + newSelection.isSelected(true); + } + if (oldSelection) { + oldSelection.isSelected(false); + } + _selectedCategory(newSelection); + } + }); + this.selectedCategory(this.rootCategory); + + this.permissionTipSubject = ko.observable(null); + + this.allCapabilitiesAsPermissions = ko.pureComputed({ + read: () => { + //Create a permission for each unique, non-deleted capability. + //Exclude special caps like do_not_allow and exist because they can't be enabled. + const excludedCaps = ['do_not_allow', 'exist']; + return _.chain(this.capabilities) + .map(function (capability) { + if (excludedCaps.indexOf(capability.name) >= 0) { + return null; + } + return new RexPermission(self, capability); + }) + .filter(function (value) { + return value !== null + }) + .value(); + }, + deferEvaluation: true + }); + + this.capsInSelectedCategory = ko.pureComputed({ + read: () => { + const category = this.selectedCategory(); + if (!category) { + return {}; + } + + let caps = {}; + category.countUniqueCapabilities(caps); + return caps; + }, + deferEvaluation: true + }); + + this.leafCategories = ko.computed({ + read: () => { + //So what we want here is a depth-first traversal of the category tree. + let results: RexCategory[] = []; + let addedUniqueCategories: AmeDictionary = {}; + + function traverse(category: RexCategory) { + if (category.subcategories.length < 1) { + //Eliminate duplicates, like CPTs that show up in the post type category and a plugin category. + let key = category.getDeDuplicationKey(); + if (!addedUniqueCategories.hasOwnProperty(key)) { + results.push(category); + addedUniqueCategories[key] = category; + } else { + addedUniqueCategories[key].addDuplicate(category); + } + return; + } + + for (let i = 0; i < category.subcategories.length; i++) { + traverse(category.subcategories[i]); + } + } + + traverse(this.rootCategory); + + results.sort(function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + + return results; + }, + deferEvaluation: true + }); + + const compareRoleDisplayNames = function (a: RexRole, b: RexRole): number { + return a.displayName().toLowerCase().localeCompare(b.displayName().toLowerCase()); + }; + this.defaultRoles = ko.pureComputed({ + read: function () { + return _.filter(self.roles(), function (role: RexRole) { + return role.isBuiltIn(); + }).sort(compareRoleDisplayNames); + }, + deferEvaluation: true + }); + this.customRoles = ko.computed({ + read: function () { + return _.difference(self.roles(), self.defaultRoles()).sort(compareRoleDisplayNames); + }, + deferEvaluation: true + }); + + this.deleteCapabilityDialog = new RexDeleteCapDialog(this); + this.addCapabilityDialog = new RexAddCapabilityDialog(this); + this.addRoleDialog = new RexAddRoleDialog(this); + this.deleteRoleDialog = new RexDeleteRoleDialog(this); + this.renameRoleDialog = new RexRenameRoleDialog(this); + this.editableRolesDialog = new RexEditableRolesDialog(this); + + this.userRoleModule = new RexUserRoleModule(this.selectedActor, this.roles); + + this.settingsFieldData = ko.observable(''); + this.isSaving = ko.observable(false); + this.isGlobalSettingsUpdate = ko.observable(false); + } + + capabilityMatchesFilters(capability: RexCapability): boolean { + if (!this.showDeprecatedEnabled() && this.isDeprecated(capability.name)) { + return false; + } + + if (this.showOnlyCheckedEnabled() && !capability.isEnabledForSelectedActor()) { + return false; + } + + const keywords = this.searchKeywords(), + capabilityName = capability.name; + if (keywords.length > 0) { + const haystack = capabilityName.toLowerCase(); + const matchesKeywords = wsAmeLodash.all( + keywords, + function (keyword) { + return haystack.indexOf(keyword) >= 0; + } + ); + + if (!matchesKeywords) { + return false; + } + } + + return true; + } + + isDeprecated(capability: string): boolean { + return this.deprecatedCapabilities.hasOwnProperty(capability); + } + + getComponent(componentId: string): RexWordPressComponent | null { + if (this.components.hasOwnProperty(componentId)) { + return this.components[componentId]; + } + return null; + } + + /** + * Get or create a capability instance. + */ + getCapability(capabilityName: string, recursionDepth: number = 0): RexCapability { + //Un-map meta capabilities where possible. + if (this.metaCapabilityMap.hasOwnProperty(capabilityName) && (recursionDepth < 10)) { + return this.getCapability(this.metaCapabilityMap[capabilityName], recursionDepth + 1); + } + + if (!this.capabilities.hasOwnProperty(capabilityName)) { + const _ = wsAmeLodash; + if (!_.isString(capabilityName) && !_.isFinite(capabilityName)) { + return this.getInvalidCapability(capabilityName); + } + + if (console && console.info) { + console.info('Capability not found: "' + capabilityName + '". It will be created.'); + } + capabilityName = String(capabilityName); + this.capabilities[capabilityName] = new RexCapability(capabilityName, this); + } + return this.capabilities[capabilityName]; + } + + private getInvalidCapability(invalidName: any): RexCapability { + const capabilityName = '[Invalid capability: ' + String(invalidName) + ']'; + if (!this.capabilities.hasOwnProperty(capabilityName)) { + if (console && console.error) { + console.error('Invalid capability detected - expected a string but got this: ', invalidName); + } + this.capabilities[capabilityName] = new RexInvalidCapability(capabilityName, invalidName, this); + } + return this.capabilities[capabilityName]; + } + + getActor(actorId: string): RexBaseActor { + if (this.actorLookup.hasOwnProperty(actorId)) { + return this.actorLookup[actorId]; + } + return this.dummyActor; + } + + getRole(name: string): RexRole { + const actorId = 'role:' + name; + if (this.actorLookup.hasOwnProperty(actorId)) { + const role = this.actorLookup[actorId]; + if (role instanceof RexRole) { + return role; + } + } + return null; + } + + // noinspection JSUnusedGlobalSymbols Testing method used in KO templates. + setSubjectPermission(permission: RexPermission) { + this.permissionTipSubject(permission); + } + + /** + * Search a string for the current search keywords and add the "rex-search-highlight" CSS class to each match. + * + * @param inputString + */ + highlightSearchKeywords(inputString: string): string { + const _ = wsAmeLodash; + const keywordList = this.searchKeywords(); + if (keywordList.length === 0) { + return inputString; + } + + let keywordGroup = _.map(keywordList, _.escapeRegExp).join('|'); + let regex = new RegExp('((?:' + keywordGroup + ')(?:\\s*))+', 'gi'); + + return inputString.replace( + regex, + function (foundKeywords) { + //Don't highlight the trailing space after the keyword(s). + let trailingSpace = ''; + let parts = foundKeywords.match(/^(.+?)(\s+)$/); + if (parts) { + foundKeywords = parts[1]; + trailingSpace = parts[2]; + } + + return '' + foundKeywords + '' + trailingSpace; + } + ); + } + + actorExists(actorId: string): boolean { + return this.actorLookup.hasOwnProperty(actorId); + } + + addUsers(newUsers: IAmeUser[]) { + wsAmeLodash.forEach(newUsers, (user) => { + if (!(user instanceof RexUser)) { + if (console.error) { + console.error('Cannot add a user. Expected an instance of RexUser, got this:', user); + } + return; + } + if (!this.actorLookup.hasOwnProperty(user.getId())) { + this.users.push(user); + this.actorLookup[user.getId()] = user; + } + }); + } + + createUserFromProperties(properties: AmeUserPropertyMap): RexUser { + return RexUser.fromAmeUserProperties(properties, this); + } + + getRoles(): AmeDictionary { + return wsAmeLodash.indexBy(this.roles(), function (role) { + return role.name(); + }); + } + + getSuperAdmin(): RexSuperAdmin { + return RexSuperAdmin.getInstance(); + } + + getUser(login: string): RexUser { + const actorId = 'user:' + login; + if (this.actorLookup.hasOwnProperty(actorId)) { + const user = this.actorLookup[actorId]; + if (user instanceof RexUser) { + return user; + } + } + return null; + } + + getUsers(): AmeDictionary { + return wsAmeLodash.indexBy(this.users(), 'userLogin'); + } + + isInSelectedCategory(capabilityName: string): boolean { + let caps = this.capsInSelectedCategory(); + return caps.hasOwnProperty(capabilityName); + } + + addCapability(capabilityName: string): RexCategory { + let capability: RexCapability; + if (this.capabilities.hasOwnProperty(capabilityName)) { + capability = this.capabilities[capabilityName]; + if (!capability.isDeleted()) { + throw 'Cannot add capability "' + capabilityName + '" because it already exists.'; + } + capability.isDeleted(false); + + this.userDefinedCapabilities[capabilityName] = true; + return null; + } else { + capability = new RexCapability(capabilityName, this); + capability.notes = 'This capability has not been saved yet. Click the "Save Changes" button to save it.'; + this.capabilities[capabilityName] = capability; + + //Add the new capability to the "Other" or "Uncategorized" category. + const category = this.categoriesBySlug['custom/uncategorized']; + const permission = new RexPermission(this, capability); + category.permissions.push(permission); + category.sortPermissions(); + + this.userDefinedCapabilities[capabilityName] = true; + return category; + } + } + + deleteCapabilities(selectedCapabilities: RexCapability[]) { + const self = this, _ = wsAmeLodash; + const targetActors = _.union (this.roles(), this.users()); + + _.forEach(selectedCapabilities, function (capability) { + //Remove it from all roles and visible users. + _.forEach(targetActors, function (actor) { + actor.deleteCap(capability.name); + }); + capability.isDeleted(true); + + delete self.userDefinedCapabilities[capability.name]; + }); + } + + capabilityExists(capabilityName: string): boolean { + return this.capabilities.hasOwnProperty(capabilityName) && !this.capabilities[capabilityName].isDeleted(); + } + + addRole(name: string, displayName: string, capabilities: CapabilityMap = {}): RexRole { + let role = new RexRole(name, displayName, capabilities); + this.actorLookup[role.id()] = role; + this.roles.push(role); + + //Select the new role. + this.selectedActor(role); + + return role; + } + + deleteRoles(roles: RexRole[]) { + const _ = wsAmeLodash; + _.forEach(roles, (role) => { + if (!this.canDeleteRole(role)) { + throw 'Cannot delete role "' + role.name() + '"'; + } + }); + + this.roles.removeAll(roles); + this.trashedRoles.push.apply(this.trashedRoles, roles); + //TODO: Later, add an option to restore deleted roles. + } + + canDeleteRole(role: RexRole): boolean { + //Was the role already assigned to any users when the editor was opened? + if (role.hasUsers) { + return false; + } + //We also need to take into account any unsaved user role changes. + //Is the role assigned to any of the users currently loaded in the editor? + const _ = wsAmeLodash; + if (_.some(this.users(), function (user) { + return (user.roles.indexOf(role) !== -1); + })) { + return false; + } + return !this.isDefaultRoleForNewUsers(role); + } + + isDefaultRoleForNewUsers(role: RexRole): boolean { + return (role.name() === this.defaultNewUserRoleName); + } + + // noinspection JSUnusedGlobalSymbols Used in KO templates. + saveChanges() { + this.isSaving(true); + const _ = wsAmeLodash; + + let data = { + 'roles': _.invoke(this.roles(), 'toJs'), + 'users': _.invoke(this.users(), 'toJs'), + 'trashedRoles': _.invoke(this.trashedRoles(), 'toJs'), + 'userDefinedCaps': _.keys(this.userDefinedCapabilities), + 'editableRoles': this.actorEditableRoles + }; + + this.settingsFieldData(ko.toJSON(data)); + jQuery('#rex-save-settings-form').submit(); + } + + updateAllSites() { + if (!confirm('Apply these role settings to ALL sites? Any changes that you\'ve made to individual sites will be lost.')) { + return false; + } + this.isGlobalSettingsUpdate(true); + this.saveChanges(); + } +} + +declare var wsRexRoleEditorData: RexAppData; + +(function () { + jQuery(function ($) { + const rootElement = jQuery('#ame-role-editor-root'); + + //Initialize the application. + const app = new RexRoleEditor(wsRexRoleEditorData); + //The input data can be quite large, so let's give the browser a chance to free up that memory. + wsRexRoleEditorData = null; + + window['ameRoleEditor'] = app; + + //console.time('Apply Knockout bindings'); + //ko.options.deferUpdates = true; + ko.applyBindings(app, rootElement.get(0)); + app.areBindingsApplied(true); + //console.timeEnd('Apply Knockout bindings'); + + //Track the state of the Shift key. + let isShiftKeyDown = false; + + function handleKeyboardEvent(event) { + const newState = !!(event.shiftKey); + if (newState !== isShiftKeyDown) { + isShiftKeyDown = newState; + app.isShiftKeyDown(isShiftKeyDown); + } + } + + $(document).on( + 'keydown.adminMenuEditorRex keyup.adminMenuEditorRex mousedown.adminMenuEditorRex', + handleKeyboardEvent + ); + + //Initialize permission tooltips. + let visiblePermissionTooltips = []; + + rootElement.find('#rex-capability-view').on('mouseenter click', '.rex-permission-tip-trigger', function (event) { + $(this).qtip({ + overwrite: false, + content: { + text: 'Loading...' + }, + + //Show the tooltip on focus. + show: { + event: 'click mouseenter', + delay: 80, + solo: '#ame-role-editor-root', + ready: true, + effect: false + }, + hide: { + event: 'mouseleave unfocus', + fixed: true, + delay: 300, + leave: false, + effect: false + }, + + position: { + my: 'center left', + at: 'center right', + effect: false, + viewport: $(window), + adjust: { + method: 'flipinvert shift', + scroll: false, + } + }, + style: { + classes: 'qtip-bootstrap qtip-shadow rex-tooltip' + }, + + events: { + show: function (event, api) { + //Immediately hide all other permission tooltips. + for (let i = visiblePermissionTooltips.length - 1; i >= 0; i--) { + visiblePermissionTooltips[i].hide(); + } + + let permission = ko.dataFor(api.target.get(0)); + if (permission && (permission instanceof RexPermission)) { + app.permissionTipSubject(permission); + } + + //Move the content container to the current tooltip. + const tipContent = $('#rex-permission-tip'); + if (!$.contains(api.elements.content.get(0), tipContent.get(0))) { + api.elements.content.empty().append(tipContent); + } + + visiblePermissionTooltips.push(api); + }, + hide: function (event, api) { + const index = visiblePermissionTooltips.indexOf(api); + if (index >= 0) { + visiblePermissionTooltips.splice(index, 1); + } + } + } + }, event); + }); + + //Tooltips must have a higher z-index than the modal widget overlay and the Toolbar. + jQuery.fn.qtip.zindex = 100101 + 5000; + + //Set up dropdown menus. + $('.rex-dropdown-trigger').on('click', function (event) { + const $trigger = $(this); + const $dropdown = $('#' + $trigger.data('target-dropdown-id')); + + event.stopPropagation(); + event.preventDefault(); + + function hideThisDropdown(event) { + //Only do it if the user clicked something outside the dropdown. + const $clickedDropdown = $(event.target).closest($dropdown.get(0)); + if ($clickedDropdown.length < 1) { + $dropdown.hide(); + $(document).off('click', hideThisDropdown); + } + } + + if ($dropdown.is(':visible')) { + $dropdown.hide(); + $(document).off('click', hideThisDropdown); + return; + } + + $dropdown.show().position({ + my: 'left top', + at: 'left bottom', + of: $trigger + }); + + $(document).on('click', hideThisDropdown); + }); + }); +})(); diff --git a/extras/modules/role-editor/uninstall.php b/extras/modules/role-editor/uninstall.php new file mode 100644 index 0000000..6faedfd --- /dev/null +++ b/extras/modules/role-editor/uninstall.php @@ -0,0 +1,6 @@ +menuEditor = $menuEditor; + ameMenu::add_custom_loader(array($this, 'loadSeparatorSettings')); + + if ( !is_admin() ) { + return; + } + + add_action('admin_menu_editor-footer-editor', array($this, 'outputDialog')); + add_action('admin_menu_editor-enqueue_styles-editor', array($this, 'enqueueStyles')); + + add_filter('ame_pre_set_custom_menu', array($this, 'addSeparatorCssToConfiguration')); + add_action('admin_enqueue_scripts', array($this, 'enqueueCustomSeparatorStyle')); + add_action('wp_ajax_' . self::CSS_AJAX_ACTION, array($this, 'ajaxOutputCss')); + } + + public function outputDialog() { + require __DIR__ . '/separator-styles-template.php'; + + wp_enqueue_auto_versioned_script( + 'ame-separator-settings-js', + plugins_url('separator-settings.js', __FILE__), + array('jquery', 'knockout', 'jquery-ui-dialog', 'jquery-ui-tabs', 'ame-ko-extensions', 'ame-lodash'), + true + ); + } + + public function enqueueStyles() { + wp_enqueue_style( + 'ame-separator-settings', + plugins_url('separator-settings.css', __FILE__), + array('menu-editor-base-style', 'wp-color-picker') + ); + } + + public function addSeparatorCssToConfiguration($customMenu) { + if ( empty($customMenu) || !is_array($customMenu) ) { + return $customMenu; + } + + if ( empty($customMenu['separators']) ) { + unset($customMenu['separator_css']); + unset($customMenu['separator_css_modified']); + return $customMenu; + } + + $css = $this->generateCss($customMenu['separators']); + $customMenu['separator_css'] = $css; + $customMenu['separator_css_modified'] = time(); + + return $customMenu; + } + + private function generateCss($settings) { + if ( empty($settings['customSettingsEnabled']) ) { + return ''; + } + + $css = $this->generateSeparatorTypeCss( + $settings['topLevelSeparators'], + '#adminmenumain #adminmenu li.wp-menu-separator .separator', + '#adminmenumain #adminmenu li.wp-menu-separator' + ); + + $css .= "\n" . '#adminmenumain #adminmenu .wp-submenu a.wp-menu-separator {' + . 'padding: 0;' + . 'margin: 0;' + . '}' . "\n"; + + $css .= $this->generateSeparatorTypeCss( + !empty($settings['useTopLevelSettingsForSubmenus']) + ? $settings['topLevelSeparators'] + : $settings['submenuSeparators'], + '#adminmenumain #adminmenu .wp-submenu .ws-submenu-separator', + '#adminmenumain #adminmenu .wp-submenu .ws-submenu-separator-wrap' + ); + + return $css; + } + + private function generateSeparatorTypeCss($settings, $nodeSelector, $parentSelector) { + $nodeSelector = trim($nodeSelector); + $parentSelector = trim($parentSelector); + + $shouldClearFloats = false; + + $parentLines = array( + 'height: auto', + 'margin: 0', + 'padding: 0', + 'width: 100%', + ); + $lines = array(); + + $separatorColor = 'transparent'; + if ( $settings['colorType'] !== 'transparent' ) { + $separatorColor = $settings['customColor']; + if ( $separatorColor === '' ) { + $separatorColor = 'transparent'; + } + } + + if ( $settings['borderStyle'] === 'solid' ) { + $lines[] = 'border: none'; + $lines[] = 'background-color: ' . $separatorColor; + $lines[] = 'height: ' . $settings['height'] . 'px'; + } else { + $lines[] = 'border-top-style: ' . $settings['borderStyle']; + + $lines[] = 'border-top-width: ' . $settings['height'] . 'px'; + $lines[] = 'height: 0'; + + $lines[] = 'border-color: ' . $separatorColor; + $lines[] = 'background: transparent'; + } + + if ( $settings['widthStrategy'] === 'percentage' ) { + $lines[] = 'width: ' . $settings['widthInPercent'] . '%'; + } else if ( $settings['widthStrategy'] === 'fixed' ) { + $lines[] = 'width: ' . $settings['widthInPixels'] . 'px'; + } + + $effectiveMargins = array( + 'top' => $settings['marginTop'] . 'px', + 'bottom' => $settings['marginBottom'] . 'px', + 'left' => $settings['marginLeft'] . 'px', + 'right' => $settings['marginRight'] . 'px', + ); + + if ( $settings['widthStrategy'] !== 'full' ) { + if ( $settings['alignment'] === 'center' ) { + $effectiveMargins['left'] = 'auto'; + $effectiveMargins['right'] = 'auto'; + } else if ( ($settings['alignment'] === 'left') || ($settings['alignment'] === 'right') ) { + $lines[] = 'float: ' . $settings['alignment']; + $shouldClearFloats = true; + } + } + + $lines[] = 'margin: ' . $effectiveMargins['top'] . ' ' . $effectiveMargins['right'] . ' ' + . $effectiveMargins['bottom'] . ' ' . $effectiveMargins['left']; + + $result = ( + $nodeSelector . " {\n" . implode(";", $lines) . ";}\n" + . $parentSelector . " {\n" . implode(";", $parentLines) . ";}\n" + ); + if ( $shouldClearFloats ) { + $result .= $parentSelector . '::after { content: ""; display: block; clear: both; height: 0; }'; + } + return $result; + } + + public function loadSeparatorSettings($menuConfig, $storedConfig) { + //Copy separator settings. + if ( isset($storedConfig['separators']) ) { + $menuConfig['separators'] = $storedConfig['separators']; + } + //Copy the pre-generated CSS. + if ( isset($storedConfig['separator_css']) && is_string($storedConfig['separator_css']) ) { + $menuConfig['separator_css'] = $storedConfig['separator_css']; + $menuConfig['separator_css_modified'] = isset($storedConfig['separator_css_modified']) + ? intval($storedConfig['separator_css_modified']) + : 0; + } + return $menuConfig; + } + + public function enqueueCustomSeparatorStyle() { + $customMenu = $this->menuEditor->load_custom_menu(); + if ( empty($customMenu) || empty($customMenu['separator_css']) ) { + return; + } + + wp_enqueue_style( + 'ame-custom-separator-styles', + add_query_arg( + 'ame_config_id', + $this->menuEditor->get_loaded_menu_config_id(), + admin_url('admin-ajax.php?action=' . urlencode(self::CSS_AJAX_ACTION)) + ), + array(), + $customMenu['separator_css_modified'] + ); + } + + public function ajaxOutputCss() { + $configId = null; + if ( isset($_GET['ame_config_id']) && !empty($_GET['ame_config_id']) ) { + $configId = (string)($_GET['ame_config_id']); + } + + $customMenu = $this->menuEditor->load_custom_menu($configId); + if ( empty($customMenu) || empty($customMenu['separator_css']) ) { + echo '/* No CSS found. */'; + return; + } + + $timestamp = $customMenu['separator_css_modified']; + //Support the If-Modified-Since header. + $omitResponseBody = false; + if ( isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ) { + $threshold = strtotime((string)$_SERVER['HTTP_IF_MODIFIED_SINCE']); + if ( $timestamp <= $threshold ) { + header('HTTP/1.1 304 Not Modified'); + $omitResponseBody = true; + } + } + + //Enable browser caching. + header('Cache-Control: public, max-age=5184000'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $timestamp) . 'GMT'); + if ( $omitResponseBody ) { + exit(); + } + + header('Content-Type: text/css'); + header('X-Content-Type-Options: nosniff'); + + echo $customMenu['separator_css']; + exit(); + } +} \ No newline at end of file diff --git a/extras/modules/separator-styles/separator-settings.css b/extras/modules/separator-styles/separator-settings.css new file mode 100644 index 0000000..da67e49 --- /dev/null +++ b/extras/modules/separator-styles/separator-settings.css @@ -0,0 +1,70 @@ +#ws-ame-separator-style-settings { + background-color: white; + padding-top: 0; } + #ws-ame-separator-style-settings input[type=number] { + width: 6em; } + #ws-ame-separator-style-settings .ame-sp-label-text { + display: inline-block; + min-width: 6em; } + #ws-ame-separator-style-settings .ame-sp-flexbox-break { + flex-basis: 100%; + height: 0; } + #ws-ame-separator-style-settings .wp-picker-container { + display: inline-block; } + #ws-ame-separator-style-settings .ame-small-tab-container { + list-style: none; + position: relative; + padding-left: 10px; + padding-top: 8px; + border-bottom: 1px solid #ddd; + background: #f9f9f9; + margin: 0 -8px 0.5em -8px; } + #ws-ame-separator-style-settings .ame-small-tab-container .ame-small-tab { + display: inline-block; + position: relative; + bottom: -1px; + margin: 0; + border: solid 1px transparent; } + #ws-ame-separator-style-settings .ame-small-tab-container .ame-small-tab a { + display: inline-block; + text-decoration: none; + padding: 5px 8px 6px 8px; } + #ws-ame-separator-style-settings .ame-small-tab-container .ame-small-tab.ame-active-tab { + border: 1px solid #ddd; + border-bottom-color: white; + background: white; } + #ws-ame-separator-style-settings .ame-small-tab-container .ame-small-tab.ame-active-tab a { + color: #444; } + #ws-ame-separator-style-settings .ame-separator-settings-container { + max-height: 500px; + overflow-y: auto; + padding-top: 0.5em; } + #ws-ame-separator-style-settings .ws_dialog_subpanel > p { + margin-top: 0; } + +#ame-separator-margins { + display: flex; + flex-wrap: wrap; + max-width: 800px; } + #ame-separator-margins .ame-sp-label-text { + min-width: 4em; } + #ame-separator-margins label { + margin-right: 2.5em; } + #ame-separator-margins input { + margin-bottom: 0.4em; } + +#ame-separator-border-styles .ame-sp-label-text { + min-width: 5em; } +#ame-separator-border-styles .ame-border-sample-container { + display: inline-block; + vertical-align: top; + min-height: 28px; } +#ame-separator-border-styles .ame-border-sample { + display: inline-block; + width: 14em; + border-top: 0.3em solid #444; } + +#ame-separator-width-options input[type=number] { + margin-top: 0.4em; } + +/*# sourceMappingURL=separator-settings.css.map */ diff --git a/extras/modules/separator-styles/separator-settings.css.map b/extras/modules/separator-styles/separator-settings.css.map new file mode 100644 index 0000000..db7398d --- /dev/null +++ b/extras/modules/separator-styles/separator-settings.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAEA,gCAAiC;EAChC,gBAAgB,EAAE,KAAK;EACvB,WAAW,EAAE,CAAC;EAEd,mDAAmB;IAClB,KAAK,EAAE,GAAG;EAGX,mDAAmB;IAClB,OAAO,EAAE,YAAY;IACrB,SAAS,EAAE,GAAG;EAGf,sDAAsB;IACrB,UAAU,EAAE,IAAI;IAChB,MAAM,EAAE,CAAC;EAGV,qDAAqB;IACpB,OAAO,EAAE,YAAY;EAGtB,yDAAyB;IAGxB,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,QAAQ;IAClB,YAAY,EAAE,IAAI;IAClB,WAAW,EAAE,GAAG;IAEhB,aAAa,EAAE,cAAyB;IACxC,UAAU,EAAE,OAAO;IAGnB,MAAM,EAAE,iBAA2D;IAEnE,wEAAe;MACd,OAAO,EAAE,YAAY;MACrB,QAAQ,EAAE,QAAQ;MAClB,MAAM,EAAE,IAAI;MAEZ,MAAM,EAAE,CAAC;MACT,MAAM,EAAE,qBAAqB;MAE7B,0EAAE;QACD,OAAO,EAAE,YAAY;QACrB,eAAe,EAAE,IAAI;QACrB,OAAO,EAAE,eAAe;MAGzB,uFAAiB;QAChB,MAAM,EAAE,cAAyB;QACjC,mBAAmB,EAAE,KAAK;QAC1B,UAAU,EAAE,KAAK;QAEjB,yFAAE;UACD,KAAK,EAAE,IAAI;EAMf,kEAAkC;IACjC,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,IAAI;IAChB,WAAW,EAAE,KAAK;EAGnB,wDAAwB;IACvB,UAAU,EAAE,CAAC;;AAIf,sBAAuB;EACtB,OAAO,EAAE,IAAI;EACb,SAAS,EAAE,IAAI;EACf,SAAS,EAAE,KAAK;EAEhB,yCAAmB;IAClB,SAAS,EAAE,GAAG;EAGf,4BAAM;IACL,YAAY,EAAE,KAAK;EAGpB,4BAAM;IACL,aAAa,EAzFa,KAAK;;AA8FhC,+CAAmB;EAClB,SAAS,EAAE,GAAG;AAGf,yDAA6B;EAC5B,OAAO,EAAE,YAAY;EACrB,cAAc,EAAE,GAAG;EACnB,UAAU,EAAE,IAAI;AAGjB,+CAAmB;EAClB,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,IAAI;EACX,UAAU,EAAE,gBAAgB;;AAK7B,+CAAmB;EAClB,UAAU,EAjHgB,KAAK", +"sources": ["separator-settings.scss"], +"names": [], +"file": "separator-settings.css" +} \ No newline at end of file diff --git a/extras/modules/separator-styles/separator-settings.js b/extras/modules/separator-styles/separator-settings.js new file mode 100644 index 0000000..43d74ee --- /dev/null +++ b/extras/modules/separator-styles/separator-settings.js @@ -0,0 +1,377 @@ +/// +/// +/// +ko.extenders['boundedInteger'] = function (target, options) { + if (options.minValue > options.maxValue) { + throw new Error('Minimum value must be smaller than the maximum value'); + } + //Create a writable computed observable to intercept writes to our observable. + var result = ko.pureComputed({ + read: target, + write: function (newValue) { + var current = target(), newInteger = parseInt(newValue); + if (isNaN(newInteger)) { + newInteger = current; + } + if (newInteger < options.minValue) { + newInteger = options.minValue; + } + if (newInteger > options.maxValue) { + newInteger = options.maxValue; + } + if (newInteger !== current) { + target(newInteger); + } + else { + //If the parsed number is the same as the current value but different from the new value, + //the new value is probably invalid or incorrectly formatted. Trigger a notification to update + //the input field with the old number. + if (String(newValue) !== String(newInteger)) { + target.notifySubscribers(current); + } + } + } + }).extend({ notify: 'always' }); + //Initialize with the current value. + result(target()); + return result; +}; +var AmeSeparatorTypeSettings = /** @class */ (function () { + function AmeSeparatorTypeSettings() { + this.defaults = { + alignment: 'none', + borderStyle: 'solid', + colorType: 'transparent', + customColor: '', + height: 5, + marginBottom: 6, + marginLeft: 0, + marginRight: 0, + marginTop: 0, + widthInPercent: 100, + widthInPixels: 160, + widthStrategy: 'full' + }; + this.colorType = ko.observable(this.defaults.colorType); + this.customColor = ko.observable(this.defaults.customColor); + this.borderStyle = ko.observable(this.defaults.borderStyle); + this.height = ko.observable(this.defaults.height).extend({ + boundedInteger: { minValue: 0, maxValue: 300 } + }); + this.widthStrategy = ko.observable(this.defaults.widthStrategy); + this.widthInPercent = ko.observable(this.defaults.widthInPercent).extend({ + boundedInteger: { minValue: 1, maxValue: 100 } + }); + this.widthInPixels = ko.observable(this.defaults.widthInPixels).extend({ + boundedInteger: { minValue: 1, maxValue: 300 } + }); + this.marginTop = ko.observable(this.defaults.marginTop).extend({ + boundedInteger: { minValue: 0, maxValue: 300 } + }); + this.marginBottom = ko.observable(this.defaults.marginBottom).extend({ + boundedInteger: { minValue: 0, maxValue: 300 } + }); + this.marginLeft = ko.observable(this.defaults.marginLeft).extend({ + boundedInteger: { minValue: 0, maxValue: 300 } + }); + this.marginRight = ko.observable(this.defaults.marginRight).extend({ + boundedInteger: { minValue: 0, maxValue: 300 } + }); + this.alignment = ko.observable(this.defaults.alignment); + } + AmeSeparatorTypeSettings.prototype.setAll = function (settings) { + var newSettings = wsAmeLodash.defaults({}, settings, this.defaults); + this.colorType(newSettings.colorType); + this.customColor(newSettings.customColor); + this.borderStyle(newSettings.borderStyle); + this.height(newSettings.height); + this.widthStrategy(newSettings.widthStrategy); + this.widthInPixels(newSettings.widthInPixels); + this.widthInPercent(newSettings.widthInPercent); + this.marginTop(newSettings.marginTop); + this.marginBottom(newSettings.marginBottom); + this.marginLeft(newSettings.marginLeft); + this.marginRight(newSettings.marginRight); + this.alignment(newSettings.alignment); + }; + AmeSeparatorTypeSettings.prototype.getAll = function () { + return { + colorType: this.colorType(), + customColor: this.customColor(), + borderStyle: this.borderStyle(), + height: this.height(), + widthStrategy: this.widthStrategy(), + widthInPercent: this.widthInPercent(), + widthInPixels: this.widthInPixels(), + marginBottom: this.marginBottom(), + marginLeft: this.marginLeft(), + marginRight: this.marginRight(), + marginTop: this.marginTop(), + alignment: this.alignment() + }; + }; + AmeSeparatorTypeSettings.prototype.resetToDefault = function () { + this.colorType(this.defaults.colorType); + this.customColor(this.defaults.customColor); + this.borderStyle(this.defaults.borderStyle); + this.height(this.defaults.height); + this.widthStrategy(this.defaults.widthStrategy); + this.widthInPixels(this.defaults.widthInPixels); + this.widthInPercent(this.defaults.widthInPercent); + this.marginTop(this.defaults.marginTop); + this.marginBottom(this.defaults.marginBottom); + this.marginLeft(this.defaults.marginLeft); + this.marginRight(this.defaults.marginRight); + this.alignment(this.defaults.alignment); + }; + return AmeSeparatorTypeSettings; +}()); +var AmeSeparatorSettingsScreen = /** @class */ (function () { + function AmeSeparatorSettingsScreen() { + var _this = this; + this.currentSavedSettings = null; + this.dialog = null; + this.customSettingsEnabled = ko.observable(false); + this.previewEnabled = ko.observable(true); + this.useTopLevelSettingsForSubmenus = ko.observable(false); + this.activeTab = ko.observable('top'); + this.topLevelSeparators = new AmeSeparatorTypeSettings(); + this.submenuSeparators = new AmeSeparatorTypeSettings(); + //As an aesthetic choice, the default margins of submenu separators shall match + //the default padding of submenu items. + this.submenuSeparators.marginTop(5); + this.submenuSeparators.marginBottom(5); + this.submenuSeparators.marginLeft(12); + this.submenuSeparators.marginRight(12); + this.currentTypeSettings = ko.computed(function () { + if (_this.activeTab() === 'top') { + return _this.topLevelSeparators; + } + else { + if (_this.useTopLevelSettingsForSubmenus()) { + return _this.topLevelSeparators; + } + else { + return _this.submenuSeparators; + } + } + }); + this.tabSettingsEnabled = ko.pureComputed(function () { + return (_this.activeTab() === 'top') || (!_this.useTopLevelSettingsForSubmenus()); + }); + this.isOpen = ko.observable(false); + this.previewCss = ko.pureComputed(function () { + if (!_this.previewEnabled() || !_this.isOpen()) { + return ''; + } + var css = _this.generatePreviewCss(_this.topLevelSeparators, '#adminmenu li.wp-menu-separator .separator', '#adminmenu li.wp-menu-separator'); + //Unlike top level separators, each submenu submenu separator is inside an element that has some + //default styles. Let's get rid of those to ensure that the separator has the correct size with respect to + //its list item parent. + css += '\n#adminmenu .wp-submenu a.wp-menu-separator {' + + 'padding: 0 !important;' + + 'margin: 0 !important;' + + '}\n'; + css += '\n' + _this.generatePreviewCss(_this.useTopLevelSettingsForSubmenus() ? _this.topLevelSeparators : _this.submenuSeparators, '#adminmenu .wp-submenu .ws-submenu-separator', '#adminmenu .wp-submenu .ws-submenu-separator-wrap'); + return css; + }); + var previewStyleTag = jQuery('').appendTo('head'); + this.previewCss.subscribe(function (css) { + previewStyleTag.text(css); + }); + } + // noinspection JSUnusedGlobalSymbols Is actually used in Knockout templates. + AmeSeparatorSettingsScreen.prototype.selectTab = function (tabId) { + if ((tabId === 'top') || (tabId === 'submenu')) { + this.activeTab(tabId); + } + return false; + }; + AmeSeparatorSettingsScreen.prototype.generatePreviewCss = function (settings, nodeSelector, parentSelector) { + nodeSelector = wsAmeLodash.trimRight(nodeSelector); + parentSelector = wsAmeLodash.trimRight(parentSelector); + var shouldClearFloats = false; + var parentLines = [ + 'height: auto', + 'margin: 0', + 'padding: 0', + 'width: 100%' + ]; + var lines = []; + var separatorColor = 'transparent'; + if (settings.colorType() !== 'transparent') { + separatorColor = settings.customColor(); + if (separatorColor === '') { + separatorColor = 'transparent'; + } + } + if (settings.borderStyle() === 'solid') { + lines.push('border: none'); + lines.push('background-color: ' + separatorColor); + lines.push('height: ' + settings.height() + 'px'); + } + else { + lines.push('border-top-style: ' + settings.borderStyle()); + lines.push('border-top-width: ' + settings.height() + 'px'); + lines.push('height: 0'); + lines.push('border-color: ' + separatorColor); + lines.push('background: transparent'); + } + if (settings.widthStrategy() === 'percentage') { + lines.push('width: ' + settings.widthInPercent() + '%'); + } + else if (settings.widthStrategy() === 'fixed') { + lines.push('width: ' + settings.widthInPixels() + 'px'); + } + var effectiveMargins = { + top: settings.marginTop() + 'px', + bottom: settings.marginBottom() + 'px', + left: settings.marginLeft() + 'px', + right: settings.marginRight() + 'px' + }; + //Alignment has no meaning for separators that take the full width of the container. Also, applying float + //would prevent the element from expanding and make it zero-width. So we apply alignment only to separators + //that have an explicitly specified width. + if (settings.widthStrategy() !== 'full') { + if (settings.alignment() === 'center') { + effectiveMargins.left = 'auto'; + effectiveMargins.right = 'auto'; + } + else if ((settings.alignment() === 'left') || (settings.alignment() === 'right')) { + lines.push('float: ' + settings.alignment()); + shouldClearFloats = true; + } + } + lines.push('margin: ' + effectiveMargins.top + ' ' + effectiveMargins.right + ' ' + + effectiveMargins.bottom + ' ' + effectiveMargins.left); + var result = (nodeSelector + ' {\n' + lines.join(' !important;\n') + ' !important;\n}\n' + + parentSelector + ' {\n' + parentLines.join(' !important;\n') + ' !important;\n}'); + if (shouldClearFloats) { + result += parentSelector + '::after { content: ""; display: block; clear: both; height: 0; }'; + } + return result; + }; + AmeSeparatorSettingsScreen.prototype.setSettings = function (settings) { + if (settings === null) { + this.applyDefaultSettings(); + return; + } + this.currentSavedSettings = wsAmeLodash.clone(settings, true); + this.topLevelSeparators.setAll(settings.topLevelSeparators); + this.submenuSeparators.setAll(settings.submenuSeparators); + this.useTopLevelSettingsForSubmenus(settings.useTopLevelSettingsForSubmenus); + this.customSettingsEnabled(settings.customSettingsEnabled); + }; + AmeSeparatorSettingsScreen.prototype.applyDefaultSettings = function () { + this.currentSavedSettings = null; + this.customSettingsEnabled(false); + this.previewEnabled(false); + this.useTopLevelSettingsForSubmenus(true); + this.topLevelSeparators.resetToDefault(); + this.submenuSeparators.resetToDefault(); + this.submenuSeparators.marginTop(5); + this.submenuSeparators.marginBottom(5); + this.submenuSeparators.marginLeft(12); + this.submenuSeparators.marginRight(12); + this.activeTab('top'); + }; + AmeSeparatorSettingsScreen.prototype.getConfirmedSettings = function () { + return this.currentSavedSettings; + }; + AmeSeparatorSettingsScreen.prototype.getDisplayedSettings = function () { + return { + topLevelSeparators: this.topLevelSeparators.getAll(), + submenuSeparators: this.submenuSeparators.getAll(), + useTopLevelSettingsForSubmenus: this.useTopLevelSettingsForSubmenus(), + customSettingsEnabled: this.customSettingsEnabled() + }; + }; + AmeSeparatorSettingsScreen.prototype.discardChanges = function () { + this.setSettings(this.currentSavedSettings); + }; + // noinspection JSUnusedGlobalSymbols + AmeSeparatorSettingsScreen.prototype.onConfirm = function () { + this.currentSavedSettings = this.getDisplayedSettings(); + if (this.dialog) { + this.dialog.dialog('close'); + } + }; + // noinspection JSUnusedGlobalSymbols + AmeSeparatorSettingsScreen.prototype.onCancel = function () { + this.discardChanges(); + if (this.dialog) { + this.dialog.dialog('close'); + } + }; + AmeSeparatorSettingsScreen.prototype.setDialog = function ($dialog) { + var _this = this; + this.dialog = $dialog; + $dialog.on('dialogopen', function () { + _this.isOpen(true); + }); + $dialog.on('dialogclose', function () { + _this.isOpen(false); + }); + }; + return AmeSeparatorSettingsScreen; +}()); +(function ($) { + var lastLoadedConfig = null; + var screen = null; + $(document) + .on('menuConfigurationLoaded.adminMenuEditor', function (event, menuConfiguration) { + //Load separator settings from the menu configuration. + if (typeof menuConfiguration['separators'] !== 'undefined') { + lastLoadedConfig = menuConfiguration['separators']; + } + else { + lastLoadedConfig = null; + } + if (screen) { + screen.setSettings(lastLoadedConfig); + } + }) + .on('getMenuConfiguration.adminMenuEditor', function (event, menuConfiguration) { + //Store separator settings in the menu configuration. + var settings = (screen !== null) ? screen.getConfirmedSettings() : lastLoadedConfig; + if (settings !== null) { + menuConfiguration['separators'] = settings; + } + else { + if (typeof menuConfiguration['separators'] !== 'undefined') { + delete menuConfiguration['separators']; + } + } + }); + jQuery(function ($) { + var separatorDialog = $('#ws-ame-separator-style-settings'); + var isDialogInitialized = false; + function initializeSeparatorDialog() { + screen = new AmeSeparatorSettingsScreen(); + if (lastLoadedConfig !== null) { + screen.setSettings(lastLoadedConfig); + } + separatorDialog.dialog({ + autoOpen: false, + closeText: ' ', + draggable: false, + modal: true, + minHeight: 400, + minWidth: 520 + }); + isDialogInitialized = true; + ko.applyBindings(screen, separatorDialog.get(0)); + screen.setDialog(separatorDialog); + } + $('#ws_edit_separator_styles').on('click', function () { + if (!isDialogInitialized) { + initializeSeparatorDialog(); + } + screen.discardChanges(); + separatorDialog.dialog('open'); + //Reset the scroll position. + separatorDialog.find('.ame-separator-settings-container').scrollTop(0); + }); + }); +})(jQuery); +//# sourceMappingURL=separator-settings.js.map \ No newline at end of file diff --git a/extras/modules/separator-styles/separator-settings.js.map b/extras/modules/separator-styles/separator-settings.js.map new file mode 100644 index 0000000..363ba86 --- /dev/null +++ b/extras/modules/separator-styles/separator-settings.js.map @@ -0,0 +1 @@ +{"version":3,"file":"separator-settings.js","sourceRoot":"","sources":["separator-settings.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/separator-styles/separator-settings.scss b/extras/modules/separator-styles/separator-settings.scss new file mode 100644 index 0000000..cadea44 --- /dev/null +++ b/extras/modules/separator-styles/separator-settings.scss @@ -0,0 +1,116 @@ +$numberInputVerticalMargin: 0.4em; + +#ws-ame-separator-style-settings { + background-color: white; + padding-top: 0; + + input[type=number] { + width: 6em; + } + + .ame-sp-label-text { + display: inline-block; + min-width: 6em; + } + + .ame-sp-flexbox-break { + flex-basis: 100%; + height: 0; + } + + .wp-picker-container { + display: inline-block; + } + + .ame-small-tab-container { + $tabBorderColor: #ddd; + + list-style: none; + position: relative; + padding-left: 10px; + padding-top: 8px; + + border-bottom: 1px solid $tabBorderColor; + background: #f9f9f9; + + $defaultDialogPadding: 8px; + margin: (0) (-$defaultDialogPadding) 0.5em (-$defaultDialogPadding); + + .ame-small-tab { + display: inline-block; + position: relative; + bottom: -1px; + + margin: 0; + border: solid 1px transparent; + + a { + display: inline-block; + text-decoration: none; + padding: 5px 8px 6px 8px; + } + + &.ame-active-tab { + border: 1px solid $tabBorderColor; + border-bottom-color: white; + background: white; + + a { + color: #444; + } + } + } + } + + .ame-separator-settings-container { + max-height: 500px; + overflow-y: auto; + padding-top: 0.5em; + } + + .ws_dialog_subpanel > p { + margin-top: 0; + } +} + +#ame-separator-margins { + display: flex; + flex-wrap: wrap; + max-width: 800px; + + .ame-sp-label-text { + min-width: 4em; + } + + label { + margin-right: 2.5em; + } + + input { + margin-bottom: $numberInputVerticalMargin; + } +} + +#ame-separator-border-styles { + .ame-sp-label-text { + min-width: 5em; + } + + .ame-border-sample-container { + display: inline-block; + vertical-align: top; + min-height: 28px; + } + + .ame-border-sample { + display: inline-block; + width: 14em; + border-top: 0.3em solid #444; + } +} + +#ame-separator-width-options { + input[type=number] { + margin-top: $numberInputVerticalMargin; + } +} \ No newline at end of file diff --git a/extras/modules/separator-styles/separator-settings.ts b/extras/modules/separator-styles/separator-settings.ts new file mode 100644 index 0000000..718b925 --- /dev/null +++ b/extras/modules/separator-styles/separator-settings.ts @@ -0,0 +1,517 @@ +/// +/// +/// + +declare var wsAmeLodash: _.LoDashStatic; + +ko.extenders['boundedInteger'] = function ( + target: KnockoutObservable , + options: { minValue: number, maxValue: number } +) { + if (options.minValue > options.maxValue) { + throw new Error('Minimum value must be smaller than the maximum value'); + } + + //Create a writable computed observable to intercept writes to our observable. + let result = ko.pureComputed({ + read: target, + write: function (newValue) { + let current = target(), + newInteger = parseInt(newValue); + + if (isNaN(newInteger)) { + newInteger = current; + } + + if (newInteger < options.minValue) { + newInteger = options.minValue; + } + if (newInteger > options.maxValue) { + newInteger = options.maxValue; + } + + if (newInteger !== current) { + target(newInteger); + } else { + //If the parsed number is the same as the current value but different from the new value, + //the new value is probably invalid or incorrectly formatted. Trigger a notification to update + //the input field with the old number. + if (String(newValue) !== String(newInteger)) { + target.notifySubscribers(current); + } + } + } + }).extend({notify: 'always'}); + + //Initialize with the current value. + result(target()); + + return result; +} + +type AmeSeparatorColorType = 'transparent' | 'custom'; +type AmeSeparatorBorderStyle = 'solid' | 'dashed' | 'dotted' | 'double'; +type AmeSeparatorWidthOption = 'full' | 'percentage' | 'fixed'; +type AmeSeparatorAlignmentOption = 'none' | 'left' | 'center' | 'right'; + +interface AmePlainSeparatorTypeSettings { + colorType: AmeSeparatorColorType; + customColor: string; + + borderStyle: AmeSeparatorBorderStyle; + height: number; + widthStrategy: AmeSeparatorWidthOption; + widthInPercent: number; + widthInPixels: number; + + marginTop: number; + marginBottom: number; + marginLeft: number; + marginRight: number; + + alignment: AmeSeparatorAlignmentOption; +} + +interface AmePlainSeparatorSettings { + topLevelSeparators: AmePlainSeparatorTypeSettings; + submenuSeparators: AmePlainSeparatorTypeSettings; + useTopLevelSettingsForSubmenus: boolean; + customSettingsEnabled: boolean; +} + +class AmeSeparatorTypeSettings implements AmeObservablePropertiesOf { + private readonly defaults: AmePlainSeparatorTypeSettings = { + alignment: 'none', + borderStyle: 'solid', + colorType: 'transparent', + customColor: '', + height: 5, + marginBottom: 6, + marginLeft: 0, + marginRight: 0, + marginTop: 0, + widthInPercent: 100, + widthInPixels: 160, + widthStrategy: 'full' + } + + colorType: KnockoutObservable ; + customColor: KnockoutObservable ; + borderStyle: KnockoutObservable ; + + height: KnockoutObservable ; + widthStrategy: KnockoutObservable ; + widthInPercent: KnockoutObservable ; + widthInPixels: KnockoutObservable ; + + marginTop: KnockoutObservable ; + marginLeft: KnockoutObservable ; + marginBottom: KnockoutObservable ; + marginRight: KnockoutObservable ; + + alignment: KnockoutObservable ; + + constructor() { + this.colorType = ko.observable(this.defaults.colorType); + this.customColor = ko.observable(this.defaults.customColor); + this.borderStyle = ko.observable(this.defaults.borderStyle); + + this.height = ko.observable(this.defaults.height).extend({ + boundedInteger: {minValue: 0, maxValue: 300} + }); + + this.widthStrategy = ko.observable(this.defaults.widthStrategy); + this.widthInPercent = ko.observable(this.defaults.widthInPercent).extend({ + boundedInteger: {minValue: 1, maxValue: 100} + }); + this.widthInPixels = ko.observable(this.defaults.widthInPixels).extend({ + boundedInteger: {minValue: 1, maxValue: 300} + }); + + this.marginTop = ko.observable(this.defaults.marginTop).extend({ + boundedInteger: {minValue: 0, maxValue: 300} + }); + this.marginBottom = ko.observable(this.defaults.marginBottom).extend({ + boundedInteger: {minValue: 0, maxValue: 300} + }); + this.marginLeft = ko.observable(this.defaults.marginLeft).extend({ + boundedInteger: {minValue: 0, maxValue: 300} + }); + this.marginRight = ko.observable(this.defaults.marginRight).extend({ + boundedInteger: {minValue: 0, maxValue: 300} + }); + + this.alignment = ko.observable(this.defaults.alignment); + } + + setAll(settings: AmePlainSeparatorTypeSettings) { + const newSettings: AmePlainSeparatorTypeSettings = wsAmeLodash.defaults({}, settings, this.defaults); + + this.colorType(newSettings.colorType); + this.customColor(newSettings.customColor); + this.borderStyle(newSettings.borderStyle); + + this.height(newSettings.height); + this.widthStrategy(newSettings.widthStrategy) + this.widthInPixels(newSettings.widthInPixels); + this.widthInPercent(newSettings.widthInPercent); + + this.marginTop(newSettings.marginTop); + this.marginBottom(newSettings.marginBottom); + this.marginLeft(newSettings.marginLeft); + this.marginRight(newSettings.marginRight); + + this.alignment(newSettings.alignment); + } + + getAll(): AmePlainSeparatorTypeSettings { + return { + colorType: this.colorType(), + customColor: this.customColor(), + borderStyle: this.borderStyle(), + + height: this.height(), + widthStrategy: this.widthStrategy(), + widthInPercent: this.widthInPercent(), + widthInPixels: this.widthInPixels(), + + marginBottom: this.marginBottom(), + marginLeft: this.marginLeft(), + marginRight: this.marginRight(), + marginTop: this.marginTop(), + + alignment: this.alignment() + }; + } + + resetToDefault() { + this.colorType(this.defaults.colorType); + this.customColor(this.defaults.customColor); + this.borderStyle(this.defaults.borderStyle); + + this.height(this.defaults.height); + this.widthStrategy(this.defaults.widthStrategy) + this.widthInPixels(this.defaults.widthInPixels); + this.widthInPercent(this.defaults.widthInPercent); + + this.marginTop(this.defaults.marginTop); + this.marginBottom(this.defaults.marginBottom); + this.marginLeft(this.defaults.marginLeft); + this.marginRight(this.defaults.marginRight); + + this.alignment(this.defaults.alignment); + } +} + +class AmeSeparatorSettingsScreen { + customSettingsEnabled: KnockoutObservable ; + previewEnabled: KnockoutObservable ; + + topLevelSeparators: AmeSeparatorTypeSettings; + submenuSeparators: AmeSeparatorTypeSettings; + useTopLevelSettingsForSubmenus: KnockoutObservable ; + + currentSavedSettings: AmePlainSeparatorSettings = null; + + currentTypeSettings: KnockoutComputed ; + + activeTab: KnockoutObservable<'top' | 'submenu'>; + /** Are the settings in the currently active tab enabled? */ + tabSettingsEnabled: KnockoutObservable ; + + previewCss: KnockoutObservable ; + + dialog: JQuery = null; + isOpen: KnockoutObservable ; + + constructor() { + this.customSettingsEnabled = ko.observable(false); + this.previewEnabled = ko.observable(true); + this.useTopLevelSettingsForSubmenus = ko.observable(false); + + this.activeTab = ko.observable('top'); + + this.topLevelSeparators = new AmeSeparatorTypeSettings(); + + this.submenuSeparators = new AmeSeparatorTypeSettings(); + //As an aesthetic choice, the default margins of submenu separators shall match + //the default padding of submenu items. + this.submenuSeparators.marginTop(5); + this.submenuSeparators.marginBottom(5); + this.submenuSeparators.marginLeft(12); + this.submenuSeparators.marginRight(12); + + this.currentTypeSettings = ko.computed(() => { + if (this.activeTab() === 'top') { + return this.topLevelSeparators; + } else { + if (this.useTopLevelSettingsForSubmenus()) { + return this.topLevelSeparators; + } else { + return this.submenuSeparators; + } + } + }); + + this.tabSettingsEnabled = ko.pureComputed(() => { + return (this.activeTab() === 'top') || (!this.useTopLevelSettingsForSubmenus()); + }); + + this.isOpen = ko.observable(false); + this.previewCss = ko.pureComputed(() => { + if (!this.previewEnabled() || !this.isOpen()) { + return ''; + } + + let css = this.generatePreviewCss( + this.topLevelSeparators, + '#adminmenu li.wp-menu-separator .separator', + '#adminmenu li.wp-menu-separator' + ); + + //Unlike top level separators, each submenu submenu separator is inside an element that has some + //default styles. Let's get rid of those to ensure that the separator has the correct size with respect to + //its list item parent. + css += '\n#adminmenu .wp-submenu a.wp-menu-separator {' + + 'padding: 0 !important;' + + 'margin: 0 !important;' + + '}\n'; + + css += '\n' + this.generatePreviewCss( + this.useTopLevelSettingsForSubmenus() ? this.topLevelSeparators : this.submenuSeparators, + '#adminmenu .wp-submenu .ws-submenu-separator', + '#adminmenu .wp-submenu .ws-submenu-separator-wrap' + ); + + return css; + }); + + let previewStyleTag = jQuery('').appendTo('head'); + this.previewCss.subscribe((css) => { + previewStyleTag.text(css); + }); + } + + // noinspection JSUnusedGlobalSymbols Is actually used in Knockout templates. + selectTab(tabId: string) { + if ((tabId === 'top') || (tabId === 'submenu')) { + this.activeTab(tabId); + } + return false; + } + + generatePreviewCss(settings: AmeSeparatorTypeSettings, nodeSelector: string, parentSelector: string): string { + nodeSelector = wsAmeLodash.trimRight(nodeSelector); + parentSelector = wsAmeLodash.trimRight(parentSelector); + + let shouldClearFloats = false; + + let parentLines = [ + 'height: auto', + 'margin: 0', + 'padding: 0', + 'width: 100%' + ]; + let lines = []; + + let separatorColor = 'transparent'; + if (settings.colorType() !== 'transparent') { + separatorColor = settings.customColor(); + if (separatorColor === '') { + separatorColor = 'transparent'; + } + } + + if (settings.borderStyle() === 'solid') { + lines.push('border: none'); + lines.push('background-color: ' + separatorColor); + lines.push('height: ' + settings.height() + 'px'); + } else { + lines.push('border-top-style: ' + settings.borderStyle()); + + lines.push('border-top-width: ' + settings.height() + 'px'); + lines.push('height: 0'); + + lines.push('border-color: ' + separatorColor); + lines.push('background: transparent'); + } + + if (settings.widthStrategy() === 'percentage') { + lines.push('width: ' + settings.widthInPercent() + '%'); + } else if (settings.widthStrategy() === 'fixed') { + lines.push('width: ' + settings.widthInPixels() + 'px'); + } + + let effectiveMargins = { + top: settings.marginTop() + 'px', + bottom: settings.marginBottom() + 'px', + left: settings.marginLeft() + 'px', + right: settings.marginRight() + 'px' + }; + + //Alignment has no meaning for separators that take the full width of the container. Also, applying float + //would prevent the element from expanding and make it zero-width. So we apply alignment only to separators + //that have an explicitly specified width. + if (settings.widthStrategy() !== 'full') { + if (settings.alignment() === 'center') { + effectiveMargins.left = 'auto'; + effectiveMargins.right = 'auto'; + } else if ((settings.alignment() === 'left') || (settings.alignment() === 'right')) { + lines.push('float: ' + settings.alignment()); + shouldClearFloats = true; + } + } + + lines.push('margin: ' + effectiveMargins.top + ' ' + effectiveMargins.right + ' ' + + effectiveMargins.bottom + ' ' + effectiveMargins.left); + + let result = ( + nodeSelector + ' {\n' + lines.join(' !important;\n') + ' !important;\n}\n' + + parentSelector + ' {\n' + parentLines.join(' !important;\n') + ' !important;\n}' + ); + if (shouldClearFloats) { + result += parentSelector + '::after { content: ""; display: block; clear: both; height: 0; }'; + } + return result; + } + + setSettings(settings: AmePlainSeparatorSettings) { + if (settings === null) { + this.applyDefaultSettings(); + return; + } + + this.currentSavedSettings = wsAmeLodash.clone(settings, true); + + this.topLevelSeparators.setAll(settings.topLevelSeparators); + this.submenuSeparators.setAll(settings.submenuSeparators); + this.useTopLevelSettingsForSubmenus(settings.useTopLevelSettingsForSubmenus); + this.customSettingsEnabled(settings.customSettingsEnabled); + } + + private applyDefaultSettings() { + this.currentSavedSettings = null; + + this.customSettingsEnabled(false); + this.previewEnabled(false); + this.useTopLevelSettingsForSubmenus(true); + + this.topLevelSeparators.resetToDefault(); + this.submenuSeparators.resetToDefault(); + + this.submenuSeparators.marginTop(5); + this.submenuSeparators.marginBottom(5); + this.submenuSeparators.marginLeft(12); + this.submenuSeparators.marginRight(12); + + this.activeTab('top'); + } + + getConfirmedSettings(): AmePlainSeparatorSettings { + return this.currentSavedSettings; + } + + getDisplayedSettings(): AmePlainSeparatorSettings { + return { + topLevelSeparators: this.topLevelSeparators.getAll(), + submenuSeparators: this.submenuSeparators.getAll(), + useTopLevelSettingsForSubmenus: this.useTopLevelSettingsForSubmenus(), + customSettingsEnabled: this.customSettingsEnabled() + }; + } + + discardChanges() { + this.setSettings(this.currentSavedSettings); + } + + // noinspection JSUnusedGlobalSymbols + onConfirm() { + this.currentSavedSettings = this.getDisplayedSettings(); + if (this.dialog) { + this.dialog.dialog('close'); + } + } + + // noinspection JSUnusedGlobalSymbols + onCancel() { + this.discardChanges(); + if (this.dialog) { + this.dialog.dialog('close'); + } + } + + setDialog($dialog: JQuery) { + this.dialog = $dialog; + $dialog.on('dialogopen', () => { + this.isOpen(true); + }); + $dialog.on('dialogclose', () => { + this.isOpen(false); + }); + } +} + +(function ($) { + let lastLoadedConfig = null; + let screen: AmeSeparatorSettingsScreen = null; + + $(document) + .on('menuConfigurationLoaded.adminMenuEditor', function (event, menuConfiguration) { + //Load separator settings from the menu configuration. + if (typeof menuConfiguration['separators'] !== 'undefined') { + lastLoadedConfig = menuConfiguration['separators']; + } else { + lastLoadedConfig = null; + } + if (screen) { + screen.setSettings(lastLoadedConfig); + } + }) + .on('getMenuConfiguration.adminMenuEditor', function (event, menuConfiguration) { + //Store separator settings in the menu configuration. + const settings = (screen !== null) ? screen.getConfirmedSettings() : lastLoadedConfig; + if (settings !== null) { + menuConfiguration['separators'] = settings; + } else { + if (typeof menuConfiguration['separators'] !== 'undefined') { + delete menuConfiguration['separators']; + } + } + }); + + jQuery(function ($) { + const separatorDialog = $('#ws-ame-separator-style-settings'); + let isDialogInitialized = false; + + function initializeSeparatorDialog() { + screen = new AmeSeparatorSettingsScreen(); + if (lastLoadedConfig !== null) { + screen.setSettings(lastLoadedConfig); + } + + separatorDialog.dialog({ + autoOpen: false, + closeText: ' ', + draggable: false, + modal: true, + minHeight: 400, + minWidth: 520 + }); + isDialogInitialized = true; + + ko.applyBindings(screen, separatorDialog.get(0)); + screen.setDialog(separatorDialog); + } + + $('#ws_edit_separator_styles').on('click', function () { + if (!isDialogInitialized) { + initializeSeparatorDialog(); + } + screen.discardChanges(); + separatorDialog.dialog('open'); + + //Reset the scroll position. + separatorDialog.find('.ame-separator-settings-container').scrollTop(0); + }); + }); +})(jQuery); diff --git a/extras/modules/separator-styles/separator-styles-template.php b/extras/modules/separator-styles/separator-styles-template.php new file mode 100644 index 0000000..3dab85b --- /dev/null +++ b/extras/modules/separator-styles/separator-styles-template.php @@ -0,0 +1,188 @@ + + diff --git a/extras/modules/super-users/super-users-template.php b/extras/modules/super-users/super-users-template.php new file mode 100644 index 0000000..8cfa77c --- /dev/null +++ b/extras/modules/super-users/super-users-template.php @@ -0,0 +1,101 @@ + ++\ No newline at end of file diff --git a/extras/modules/super-users/super-users.css b/extras/modules/super-users/super-users.css new file mode 100644 index 0000000..830eaf1 --- /dev/null +++ b/extras/modules/super-users/super-users.css @@ -0,0 +1,4 @@ +.ame-column-user-id { + width: 70px; } + +/*# sourceMappingURL=super-users.css.map */ diff --git a/extras/modules/super-users/super-users.css.map b/extras/modules/super-users/super-users.css.map new file mode 100644 index 0000000..f5d5d7d --- /dev/null +++ b/extras/modules/super-users/super-users.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAAA,mBAAoB;EACnB,KAAK,EAAE,IAAI", +"sources": ["super-users.scss"], +"names": [], +"file": "super-users.css" +} \ No newline at end of file diff --git a/extras/modules/super-users/super-users.js b/extras/modules/super-users/super-users.js new file mode 100644 index 0000000..96692a8 --- /dev/null +++ b/extras/modules/super-users/super-users.js @@ -0,0 +1,88 @@ +///+ Hidden Users + Add +
+ ++ +
+ + + + + ++ + + + + +Username +Name +Role +ID ++ + + + + + ++ + + + ++ Remove +++ + + + + + + + ++ No users selected. Click "" to hide one or more users. + ++ + +Username +Name +Role +ID ++/// +/// +/// +/// +var AmeSuperUsers = /** @class */ (function () { + function AmeSuperUsers(settings) { + var _this = this; + this.addButtonText = 'Add User'; + this.userEditUrl = settings.userEditUrl; + this.currentUserLogin = settings.currentUserLogin; + this.superUsers = ko.observableArray([]); + AmeSuperUsers._.forEach(settings.superUsers, function (userDetails) { + var user = AmeUser.createFromProperties(userDetails); + if (!AmeActors.getUser(user.userLogin)) { + AmeActors.addUsers([user]); + } + _this.superUsers.push(user); + }); + this.superUsers.sort(AmeSuperUsers.compareLogins); + this.settingsData = ko.computed(function () { + return AmeSuperUsers._.map(_this.superUsers(), 'userId').join(','); + }); + //Store the state of the info box in a cookie. + var initialState = jQuery.cookie('ame_su_info_box_open'); + var _isBoxOpen = ko.observable((typeof initialState === 'undefined') ? true : (initialState === '1')); + this.isInfoBoxOpen = ko.computed({ + read: function () { + return _isBoxOpen(); + }, + write: function (value) { + jQuery.cookie('ame_su_info_box_open', value ? '1' : '0', { expires: 90 }); + _isBoxOpen(value); + } + }); + } + AmeSuperUsers.prototype.removeUser = function (user) { + this.superUsers.remove(user); + }; + AmeSuperUsers.prototype.getEditLink = function (user) { + return this.userEditUrl + '?user_id=' + user.userId; + }; + AmeSuperUsers.prototype.selectHiddenUsers = function () { + var _this = this; + AmeSelectUsersDialog.open({ + selectedUsers: AmeSuperUsers._.map(this.superUsers(), 'userLogin'), + users: AmeSuperUsers._.indexBy(this.superUsers(), 'userLogin'), + actorManager: AmeActors, + currentUserLogin: this.currentUserLogin, + alwaysIncludeCurrentUser: false, + save: function (selectedUsers) { + selectedUsers.sort(AmeSuperUsers.compareLogins); + _this.superUsers(selectedUsers); + } + }); + }; + AmeSuperUsers.compareLogins = function (a, b) { + if (a.userLogin > b.userLogin) { + return 1; + } + else if (a.userLogin < b.userLogin) { + return -1; + } + return 0; + }; + AmeSuperUsers.prototype.formatUserRoles = function (user) { + var displayNames = AmeSuperUsers._.map(user.roles, function (roleId) { + var actor = AmeActors.getActor('role:' + roleId); + if (actor) { + return actor.displayName; + } + else { + return '[Unknown role]'; + } + }); + return displayNames.join(', '); + }; + AmeSuperUsers.prototype.toggleInfoBox = function () { + this.isInfoBoxOpen(!this.isInfoBoxOpen()); + }; + AmeSuperUsers._ = wsAmeLodash; + return AmeSuperUsers; +}()); +jQuery(function () { + var superUserVM = new AmeSuperUsers(wsAmeSuperUserSettings); + ko.applyBindings(superUserVM, document.getElementById('ame-super-user-settings')); +}); +//# sourceMappingURL=super-users.js.map \ No newline at end of file diff --git a/extras/modules/super-users/super-users.js.map b/extras/modules/super-users/super-users.js.map new file mode 100644 index 0000000..9f9285a --- /dev/null +++ b/extras/modules/super-users/super-users.js.map @@ -0,0 +1 @@ +{"version":3,"file":"super-users.js","sourceRoot":"","sources":["super-users.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/super-users/super-users.php b/extras/modules/super-users/super-users.php new file mode 100644 index 0000000..dd3c91f --- /dev/null +++ b/extras/modules/super-users/super-users.php @@ -0,0 +1,243 @@ +getSuperUserIDs(); + if ( empty($superUsers) ) { + return $args; + } + + if ( !$this->isCurrentUserSuper() ) { + $args['exclude'] = array_merge( + isset($args['exclude']) ? $args['exclude'] : array(), + $superUsers + ); + + //Exclude hidden users even if specifically included. This can happen + //when looking at the "None" view on the "Users" page (this view shows + //users that have no role on the current site). + if ( isset($args['include']) && !empty($args['include']) ) { + $args['include'] = array_diff($args['include'], $superUsers); + if ( empty($args['include']) ) { + unset($args['include']); + } + } + } + + return $args; + } + + /** + * Prevent normal users from editing superusers. + * + * @param string[] $requiredCaps List of primitive capabilities (output). + * @param string $capability The meta capability (input). + * @param int $thisUserId The user that's trying to do something. + * @param array $args + * @return string[] + */ + public function restrictUserEditing($requiredCaps, $capability, $thisUserId, $args) { + static $editUserCaps = array('edit_user', 'delete_user', 'promote_user', 'remove_user'); + if ( !in_array($capability, $editUserCaps) || !isset($args[0]) ) { + return $requiredCaps; + } + + /** @var int The user that might be edited or deleted. */ + $targetUserId = intval($args[0]); + $thisUserId = intval($thisUserId); + + if ( $this->isSuperUser($targetUserId) && !$this->isSuperUser($thisUserId) ) { + return array_merge($requiredCaps, array('do_not_allow')); + } + + return $requiredCaps; + } + + /** + * Filter the user counts shown in the list of roles at the top of the "Users" page. + * + * @param $result + * @param $strategy + * @param $siteId + * @return array|null + */ + public function filterUserCounts($result = null, $strategy = 'time', $siteId = null) { + //We're going to call count_users() which will trigger the 'pre_count_users' filter + //again, so we need to avoid infinite recursion. + if ( $this->isInsideCountFilter ) { + return $result; + } + + //Perform this filtering only on the "Users" page. + if ( !isset($GLOBALS['parent_file']) || ($GLOBALS['parent_file'] !== 'users.php') ) { + return $result; + } + + if ( $this->isCurrentUserSuper() ) { + //This user can see other hidden users. + return $result; + } + + $superUsers = $this->getSuperUsers($siteId); + //Note the $siteId. We want the roles that each user has on the specified site. + //This should normally be the current site, but it doesn't have to be. + + if ( empty($superUsers) ) { + //There are no users that need to be hidden. + return $result; + } + + /** @noinspection PhpFieldImmediatelyRewrittenInspection Recursive filters! */ + $this->isInsideCountFilter = true; + $result = count_users($strategy, $siteId); + + //Adjust the total number of users. + $result['total_users'] -= count($superUsers); + + //For each hidden user, subtract one from each of the roles that the user has. + foreach ($superUsers as $user) { + if ( !empty($user->roles) && is_array($user->roles) ) { + foreach ($user->roles as $roleId) { + if ( isset($result['avail_roles'][$roleId]) ) { + $result['avail_roles'][$roleId]--; + if ( $result['avail_roles'][$roleId] <= 0 ) { + unset($result['avail_roles'][$roleId]); + } + } + } + } else if ( isset($result['avail_roles']['none']) ) { + $result['avail_roles']['none']--; + } + } + + $this->isInsideCountFilter = false; + return $result; + } + + /** + * @return int[] + */ + private function getSuperUserIDs() { + $result = $this->menuEditor->get_plugin_option('super_users'); + if ( $result === null ) { + return array(); + } + return $result; + } + + /** + * @param int|null $siteId + * @return WP_User[] + */ + private function getSuperUsers($siteId = null) { + $ids = $this->getSuperUserIDs(); + if ( empty($ids) ) { + return array(); + } + + if ( !is_numeric($siteId) ) { + $siteId = get_current_blog_id(); + } + + //Caution: If you pass an empty array as "include", get_users() will return *all* users from the current site. + return get_users( array( + 'include' => $ids, + 'blog_id' => $siteId, + ) ); + } + + /** + * Is the current user one of the superusers? + * + * @return bool + */ + private function isCurrentUserSuper() { + $user = wp_get_current_user(); + return $user && $this->isSuperUser($user->ID); + } + + private function isSuperUser($userId) { + return in_array($userId, $this->getSuperUserIDs()); + } + + public function enqueueTabScripts() { + parent::enqueueTabScripts(); + + wp_enqueue_auto_versioned_script( + 'ame-super-users', + plugins_url('super-users.js', __FILE__), + array('knockout', 'jquery', 'ame-visible-users', 'ame-actor-manager', 'ame-jquery-cookie') + ); + + //Pass users to JS. + $users = array(); + foreach($this->getSuperUsers() as $user) { + $properties = $this->menuEditor->user_to_property_map($user); + $properties['avatar_html'] = get_avatar($user->ID, 32); + $users[$user->user_login] = $properties; + } + + $currentUser = wp_get_current_user(); + wp_localize_script( + 'ame-super-users', + 'wsAmeSuperUserSettings', + array( + 'superUsers' => $users, + 'userEditUrl' => admin_url('user-edit.php'), + 'currentUserLogin' => $currentUser->get('user_login'), + ) + ); + } + + public function enqueueTabStyles() { + parent::enqueueTabStyles(); + + wp_enqueue_auto_versioned_style( + 'ame-super-users-css', + plugins_url('super-users.css', __FILE__) + ); + } + + public function handleFormSubmission($action, $post = array()) { + //Note: We don't need to check user permissions here because plugin core already did. + if ( $action === 'ame_save_super_users' ) { + check_admin_referer($action); + + $userIDs = array_map('intval', explode(',', $post['settings'], 100)); + $userIDs = array_unique(array_filter($userIDs)); + + //Save settings. + $this->menuEditor->set_plugin_option('super_users', $userIDs); + + wp_redirect($this->getTabUrl(array('message' => 1))); + exit; + } + } +} \ No newline at end of file diff --git a/extras/modules/super-users/super-users.scss b/extras/modules/super-users/super-users.scss new file mode 100644 index 0000000..9976714 --- /dev/null +++ b/extras/modules/super-users/super-users.scss @@ -0,0 +1,3 @@ +.ame-column-user-id { + width: 70px; +} \ No newline at end of file diff --git a/extras/modules/super-users/super-users.ts b/extras/modules/super-users/super-users.ts new file mode 100644 index 0000000..b67e606 --- /dev/null +++ b/extras/modules/super-users/super-users.ts @@ -0,0 +1,107 @@ +/// +/// +/// +/// +/// + +declare var wsAmeSuperUserSettings: Object; +declare var AmeSelectUsersDialog: any; + +class AmeSuperUsers { + private static _ = wsAmeLodash; + public superUsers: KnockoutObservableArray ; + public settingsData: KnockoutComputed ; + + private userEditUrl: string; + private currentUserLogin: string; + + public addButtonText: string = 'Add User'; + public isInfoBoxOpen: KnockoutComputed ; + + constructor(settings) { + this.userEditUrl = settings.userEditUrl; + this.currentUserLogin = settings.currentUserLogin; + + this.superUsers = ko.observableArray([]); + AmeSuperUsers._.forEach(settings.superUsers, (userDetails) => { + var user = AmeUser.createFromProperties(userDetails); + if (!AmeActors.getUser(user.userLogin)) { + AmeActors.addUsers([user]); + } + this.superUsers.push(user); + }); + this.superUsers.sort(AmeSuperUsers.compareLogins); + + this.settingsData = ko.computed ((): string => { + return AmeSuperUsers._.map(this.superUsers(), 'userId').join(','); + }); + + //Store the state of the info box in a cookie. + let initialState = jQuery.cookie('ame_su_info_box_open'); + let _isBoxOpen = ko.observable ((typeof initialState === 'undefined') ? true : (initialState === '1')); + + this.isInfoBoxOpen = ko.computed ({ + read: (): boolean => { + return _isBoxOpen(); + }, + write: (value: boolean) => { + jQuery.cookie('ame_su_info_box_open', value ? '1' : '0', {expires: 90}); + _isBoxOpen(value); + } + }); + } + + public removeUser(user: AmeUser) { + this.superUsers.remove(user); + } + + public getEditLink(user: AmeUser) { + return this.userEditUrl + '?user_id=' + user.userId; + } + + public selectHiddenUsers() { + AmeSelectUsersDialog.open({ + selectedUsers: AmeSuperUsers._.map(this.superUsers(), 'userLogin'), + users: AmeSuperUsers._.indexBy(this.superUsers(), 'userLogin'), + actorManager: AmeActors, + + currentUserLogin: this.currentUserLogin, + alwaysIncludeCurrentUser: false, + + save: (selectedUsers: AmeUser[]) => { + selectedUsers.sort(AmeSuperUsers.compareLogins); + this.superUsers(selectedUsers); + } + }); + } + + private static compareLogins(a: AmeUser, b: AmeUser): number { + if (a.userLogin > b.userLogin) { + return 1; + } else if (a. userLogin < b.userLogin) { + return -1; + } + return 0; + } + + public formatUserRoles(user: AmeUser): string { + let displayNames = AmeSuperUsers._.map(user.roles, (roleId) => { + var actor = AmeActors.getActor('role:' + roleId); + if (actor) { + return actor.displayName; + } else { + return '[Unknown role]'; + } + }); + return displayNames.join(', '); + } + + public toggleInfoBox() { + this.isInfoBoxOpen(!this.isInfoBoxOpen()); + } +} + +jQuery(function() { + var superUserVM = new AmeSuperUsers(wsAmeSuperUserSettings); + ko.applyBindings(superUserVM, document.getElementById('ame-super-user-settings')); +}); diff --git a/extras/modules/tweaks/ameAdminCssTweakManager.php b/extras/modules/tweaks/ameAdminCssTweakManager.php new file mode 100644 index 0000000..cb025c3 --- /dev/null +++ b/extras/modules/tweaks/ameAdminCssTweakManager.php @@ -0,0 +1,70 @@ +pendingCss[] = $settings['css']; + if ( !$this->isOutputHookRegistered ) { + add_action('admin_print_scripts', array($this, 'outputCss')); + $this->isOutputHookRegistered = true; + } + } + + public function outputCss() { + if ( empty($this->pendingCss) ) { + return; + } + echo '', "\n"; + echo '', "\n"; + } + + /** + * Create a CSS tweak instance with the specified properties. + * + * @param array $properties + * @return ameDelegatedTweak + */ + public function createTweak($properties) { + if ( $this->cachedUserInput === null ) { + $this->cachedUserInput = (new StringSetting('css'))->textarea('css'); + } + + $cssTweak = new ameDelegatedTweak( + $properties['id'], + $properties['label'], + array($this, 'enqueueCss') + ); + $cssTweak->setSectionId('admin-css'); + $cssTweak->add($this->cachedUserInput); + + return $cssTweak; + } + + /** + * @param ameTweakManager $tweakManager + */ + public function registerDefaultTweak($tweakManager) { + $tweakManager->addSection('admin-css', 'Admin CSS', 20); + + $defaultTweak = $this->createTweak(array( + 'id' => 'default-admin-css', + 'label' => 'Add custom admin CSS', + )); + $tweakManager->addTweak($defaultTweak); + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameBaseTweak.php b/extras/modules/tweaks/ameBaseTweak.php new file mode 100644 index 0000000..e4069cc --- /dev/null +++ b/extras/modules/tweaks/ameBaseTweak.php @@ -0,0 +1,123 @@ +id; + } + + public function getLabel() { + return $this->label; + } + + public function getParentId() { + return $this->parentId; + } + + public function setParentId($id) { + $this->parentId = $id; + return $this; + } + + public function setSectionId($id) { + $this->sectionId = $id; + return $this; + } + + public function getSectionId() { + return $this->sectionId; + } + + public function hasScreenFilter() { + return ($this->screens !== null); + } + + public function isEnabledForCurrentScreen() { + if ( !$this->hasScreenFilter() ) { + return true; + } + if ( !function_exists('get_current_screen') ) { + return false; + } + $screen = get_current_screen(); + if ( isset($screen, $screen->id) ) { + return $this->isEnabledForScreen($screen->id); + } + return false; + } + + public function isEnabledForScreen($screenId) { + if ( $this->screens === null ) { + return true; + } + return in_array($screenId, $this->screens); + } + + public function setScreens($screens) { + $this->screens = $screens; + } + + public function isIndependentlyHideable() { + return ($this->hideableCategory !== null); + } + + public function getHideableCategoryId() { + return $this->hideableCategory; + } + + public function setHideableCategoryId($categoryId) { + $this->hideableCategory = $categoryId; + } + + public function getHideableLabel() { + if ( $this->hideableLabel !== null ) { + return $this->hideableLabel; + } + return $this->getLabel(); + } + + public function setHideableLabel($text) { + $this->hideableLabel = $text; + } + + public function toArray() { + return array_merge( + parent::toArray(), + array( + 'parentId' => $this->getParentId(), + 'sectionId' => $this->getSectionId(), + ) + ); + } + + public function supportsUserInput() { + return $this->hasAnySettings(); + } + + //todo: getEditableProperties(). Or maybe we don't need it at all? Just merge the settings. +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameDelegatedTweak.php b/extras/modules/tweaks/ameDelegatedTweak.php new file mode 100644 index 0000000..4ac10e3 --- /dev/null +++ b/extras/modules/tweaks/ameDelegatedTweak.php @@ -0,0 +1,32 @@ +callback = $callback; + + if ( !is_array($callbackArgs) ) { + throw new LogicException('$callbackArgs must be an array'); + } + $this->callbackArgs = $callbackArgs; + } + + public function apply($settings = null) { + $theArgs = $this->callbackArgs; + if ( $settings !== null ) { + $theArgs[] = $settings; + } + call_user_func_array($this->callback, $theArgs); + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameEnvironmentColorTweak.php b/extras/modules/tweaks/ameEnvironmentColorTweak.php new file mode 100644 index 0000000..5f335e1 --- /dev/null +++ b/extras/modules/tweaks/ameEnvironmentColorTweak.php @@ -0,0 +1,187 @@ + true); + if ( function_exists('is_multisite') && is_multisite() ) { + $toolbarDefaults['special:super_admin'] = true; + } + + $this + ->add( + (new SettingsGroup('colors', 'Environments:')) + ->add(new ColorSetting('production', 'Production')) + ->add(new ColorSetting('staging', 'Staging')) + ->add(new ColorSetting('development', 'Development')) + ->add(new ColorSetting('local', 'Local')) + ) + ->add( + (new SettingsGroup('targets', 'Apply color to:', null)) + ->add( + (new ActorFeature('colorizeToolbar', 'Toolbar (a.k.a Admin Bar)')) + ->setDefaultAccessMap($toolbarDefaults) + ) + ->add(new ActorFeature('colorizeAdminMenu', 'Admin menu')) + ); + } + + public function apply($settings = null) { + if ( !function_exists('wp_get_environment_type') ) { + return; + } + $environment = wp_get_environment_type(); + if ( empty($environment) ) { + return; + } + + if ( empty($settings['colors'][$environment]) ) { + return; + } + + $this->chosenColor = trim($settings['colors'][$environment]); + if ( !$this->isValidCssColor($this->chosenColor) ) { + $this->chosenColor = ''; + return; + } + + if ( + isset($GLOBALS['wsMenuEditorExtras']) + && method_exists($GLOBALS['wsMenuEditorExtras'], 'check_current_user_access') + ) { + $extras = $GLOBALS['wsMenuEditorExtras']; + /** @var wsMenuEditorExtras $extras */ + + $this->colorizeComponent['toolbar'] = $extras->check_current_user_access( + ameUtils::get($settings, 'colorizeToolbar', array()) + ); + $this->colorizeComponent['adminMenu'] = $extras->check_current_user_access( + ameUtils::get($settings, 'colorizeAdminMenu', array()) + ); + } + + if ( did_action('admin_bar_init') ) { + $this->enqueueEnvironmentStyle(); + } else { + add_action('admin_bar_init', array($this, 'enqueueEnvironmentStyle')); + } + } + + public function enqueueEnvironmentStyle() { + $customizations = array( + 'toolbar' => array( + 'background' => array( + '#wpadminbar', + '#wpadminbar .ab-item', + '#wpadminbar .ab-sub-wrapper', + '#wpadminbar .ab-sub-secondary', + ), + 'text' => array( + '#wpadminbar .ab-empty-item', + '#wpadminbar a.ab-item', + '#wpadminbar > #wp-toolbar span.ab-label', + '#wpadminbar > #wp-toolbar span.noticon', + + '#wpadminbar .ab-icon::before', + '#wpadminbar .ab-item::before', + '#wpadminbar #adminbarsearch::before', + ), + 'styleHandle' => 'admin-bar', + ), + 'adminMenu' => array( + 'background' => array( + '#adminmenuback', + '#adminmenuwrap', + '#adminmenu', + '#adminmenu .wp-submenu', + ), + 'text' => array( + '#adminmenu a', + '#adminmenu div.wp-menu-image::before', + 'div.wp-menu-image::before', + '#adminmenu .wp-submenu a', + '#collapse-button', + ), + 'styleHandle' => 'admin-menu', + ), + ); + + $textColor = $this->getContrastingTextColor($this->chosenColor); + + foreach ($customizations as $component => $details) { + if ( empty($this->colorizeComponent[$component]) ) { + continue; + } + + $css = sprintf( + '%1$s { background-color: %2$s !important; }', + implode(',', $details['background']), + $this->chosenColor + ); + + if ( $textColor !== null ) { + $css .= sprintf( + '%1$s { color: %2$s !important; }', + implode(', ', $details['text']), + $textColor + ); + } + + wp_add_inline_style($details['styleHandle'], $css); + } + } + + private function isValidCssColor($color) { + if ( !is_string($color) ) { + return false; + } + return (preg_match('@^#[0-9a-f]{3,8}$@i', $color) === 1); + } + + /** + * @param string $backgroundColor + * @return string|null A hex color value, or NULL to leave the color unchanged. + */ + private function getContrastingTextColor($backgroundColor) { + $colorLibraryPath = AME_ROOT_DIR . '/extras/phpColors/src/color.php'; + if ( !class_exists('phpColor', false) && file_exists($colorLibraryPath) ) { + /** @noinspection PhpIncludeInspection */ + include($colorLibraryPath); + } + if ( !class_exists('phpColor') ) { + return null; + } + + //The default admin color scheme uses a very light grey as the text color for the Toolbar + //and the admin menu. If the user chooses a light background color, this could make text + //difficult to read. To avoid that, let's automatically change the text color to dark grey + //if the background color is too light. + + //TODO: This doesn't really work correctly because phpColor doesn't seem to use the same HSL space as other tools. + //Maybe replace it with something else. + + try { + $background = phpColor::hexToHsl($backgroundColor); + if ( $background['L'] > 0.4 ) { + //Text needs to be darker. + return '#101010'; + } + } catch (Exception $e) { + return null; + } + return null; + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameEnvironmentNameTweak.php b/extras/modules/tweaks/ameEnvironmentNameTweak.php new file mode 100644 index 0000000..acd3775 --- /dev/null +++ b/extras/modules/tweaks/ameEnvironmentNameTweak.php @@ -0,0 +1,74 @@ +currentEnvironment = wp_get_environment_type(); + if ( empty($this->currentEnvironment) ) { + return; + } + + add_action('admin_bar_menu', array($this, 'addEnvironmentToToolbar')); + } + + /** + * @param WP_Admin_Bar|null $adminBar + */ + public function addEnvironmentToToolbar($adminBar = null) { + if ( !$adminBar ) { + return; + } + + //Dashicons for different environments. Some icons are not aligned in the same way as others, + //so we also store a relative offset (position) from the top of the box. + $iconsByEnvironment = array( + 'production' => array('f11f', 3), + 'staging' => array('f463', 3), + 'development' => array('f107', 3), + 'local' => array('f102', 2), + ); + + $icon = 'f159'; + $offsetTop = 3; + if ( !empty($iconsByEnvironment[$this->currentEnvironment]) ) { + list($icon, $offsetTop) = $iconsByEnvironment[$this->currentEnvironment]; + } + + $itemId = 'ame-tweak-environment-type'; + $this->toolbarIconCss = sprintf( + '#wp-admin-bar-%s .ab-icon::before { + content: "\\%s"; + top: %dpx; + }', + $itemId, + $icon, + $offsetTop + ); + + $iconHtml = ''; + $adminBar->add_node(array( + 'id' => $itemId, + 'title' => $iconHtml . esc_html(ameUtils::ucWords($this->currentEnvironment)), + 'parent' => 'top-secondary', + 'meta' => array( + 'title' => 'Current environment type', + ), + )); + + add_action('wp_before_admin_bar_render', array($this, 'printToolbarIconCss')); + } + + public function printToolbarIconCss() { + printf('', $this->toolbarIconCss); + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameGutenbergBlockManager.php b/extras/modules/tweaks/ameGutenbergBlockManager.php new file mode 100644 index 0000000..5021f72 --- /dev/null +++ b/extras/modules/tweaks/ameGutenbergBlockManager.php @@ -0,0 +1,235 @@ +menuEditor = $menuEditor; + if ( is_admin() ) { + add_action('enqueue_block_editor_assets', array($this, 'enqueueGutenbergAssets'), 10000); + add_action('wp_ajax_' . self::UPDATE_BLOCKS_ACTION, array($this, 'ajaxUpdateBlocks')); + } + + add_action('admin-menu-editor-register_tweaks', array($this, 'registerBlockTweaks'), 10, 2); + + //The "allowed_block_types" filter was deprecated in WP 5.8 and a new "allowed_block_types_all" + //filter was introduced. Note that the filters take different arguments, but we can ignore that + //in this case because the first argument is the same and that's all we need here. + global $wp_version; + if ( isset($wp_version) && is_string($wp_version) && version_compare($wp_version, '5.8', '<') ) { + $blockFilter = 'allowed_block_types'; //Deprecated since WP 5.8.0. + } else { + $blockFilter = 'allowed_block_types_all'; + } + add_filter($blockFilter, array($this, 'filterAllowedBlocks'), 10000, 1); + } + + public function enqueueGutenbergAssets() { + //To reduce the performance impact of this feature, we only detect new Gutenberg blocks + //for users that can access the plugin. + if ( !$this->menuEditor->current_user_can_edit_menu() ) { + return; + } + + wp_enqueue_script( + self::SCRIPT_HANDLE, + plugins_url('gutenberg-block-detector.js', __FILE__), + array('wp-blocks', 'wp-dom-ready', 'wp-edit-post', 'jquery'), + '20210218-4', + true + ); + + $detectedItems = $this->getDetectedItems(); + $scriptData = array( + 'knownBlocks' => array_fill_keys(array_keys($detectedItems['blocks']), true), + 'knownCategories' => array_fill_keys(array_keys($detectedItems['categories']), true), + 'ajaxUrl' => self_admin_url('admin-ajax.php'), + 'ajaxAction' => self::UPDATE_BLOCKS_ACTION, + 'updateNonce' => wp_create_nonce('ws_ame_update_gtb_blocks'), + ); + + //Make sure to encode associative arrays as objects (dictionaries) even when they're empty. + if ( empty($scriptData['knownBlocks']) ) { + $scriptData['knownBlocks'] = new stdClass(); + } + if ( empty($scriptData['knownCategories']) ) { + $scriptData['knownCategories'] = new stdClass(); + } + + wp_localize_script(self::SCRIPT_HANDLE, 'wsAmeGutenbergBlockData', $scriptData); + } + + /** @noinspection PhpComposerExtensionStubsInspection */ + public function ajaxUpdateBlocks() { + check_ajax_referer(self::UPDATE_BLOCKS_ACTION); + + @header('Content-Type: application/json; charset=' . get_option('blog_charset')); + if ( !$this->menuEditor->current_user_can_edit_menu() ) { + echo json_encode(array('error' => 'Access denied')); + exit; + } + + //Basic validation. + $post = $this->menuEditor->get_post_params(); + if ( !isset($post['blocks'], $post['categories']) ) { + echo json_encode(array('error' => 'The "blocks" or "categories" field is missing.')); + exit; + } + $blocks = json_decode($post['blocks'], true); + $categories = json_decode($post['categories'], true); + if ( ($blocks === null) || ($categories === null) ) { + echo json_encode(array('error' => 'The "blocks" or "categories" field is not valid JSON.')); + exit; + } + + $this->saveDetectedItems($blocks, $categories); + echo json_encode(array('success' => true)); + exit; + } + + private function saveDetectedItems($blocks, $categories) { + //Index the lists by name or slug. + $blockIndex = array(); + foreach ($blocks as $block) { + $name = $block['name']; + unset($block['name']); + $blockIndex[$name] = $block; + } + $categoryIndex = array(); + foreach ($categories as $category) { + $slug = $category['slug']; + unset($category['slug']); + $categoryIndex[$slug] = $category; + } + + $data = array( + 'blocks' => $blockIndex, + 'categories' => $categoryIndex, + ); + + $lock = ameFileLock::create(__FILE__); + if ( !$lock->acquire() ) { + return; + } + + if ( is_multisite() ) { + update_site_option(self::DETECTED_BLOCK_OPTION, $data); + } else { + update_option(self::DETECTED_BLOCK_OPTION, $data, true); + } + + $lock->release(); + } + + private function getDetectedItems() { + $default = array('blocks' => array(), 'categories' => array()); + if ( is_multisite() ) { + $data = get_site_option(self::DETECTED_BLOCK_OPTION, $default); + } else { + $data = get_option(self::DETECTED_BLOCK_OPTION, $default); + } + if ( !is_array($data) ) { + return $default; + } + return $data; + } + + /** + * @param ameTweakManager $manager + * @param null|array $tweakFilter + */ + public function registerBlockTweaks($manager, $tweakFilter = null) { + $data = $this->getDetectedItems(); + $blocks = ameUtils::get($data, 'blocks', array()); + if ( empty($blocks) ) { + //The user must first open the Gutenberg editor so that we can detect registered blocks. + return; + } + + $manager->addSection(self::SECTION_ID, 'Hide Gutenberg Blocks', 40); + + if ( $tweakFilter !== null ) { + $filteredBlocks = array(); + foreach ($blocks as $id => $data) { + if ( isset($tweakFilter[self::TWEAK_PREFIX . $id]) ) { + $filteredBlocks[$id] = $data; + } + } + $blocks = $filteredBlocks; + } + + if ( $tweakFilter === null ) { + //Create stub tweaks that represent each block category. + $categories = ameUtils::get($data, 'categories', array()); + foreach ($categories as $catId => $category) { + $parentTweak = new ameDelegatedTweak( + self::PARENT_PREFIX . $catId, + ameUtils::get($category, 'title', $catId), + '__return_false' //This tweak is just a presentation tool. It doesn't do anything. + ); + $parentTweak->setSectionId(self::SECTION_ID); + $manager->addTweak($parentTweak); + } + } + + $theCallback = array($this, 'flagBlockAsHidden'); + foreach ($blocks as $id => $block) { + $tweak = new ameDelegatedTweak( + self::TWEAK_PREFIX . $id, + ameUtils::get($block, 'title', $id), + $theCallback, + array($id) + ); + $tweak->setSectionId(self::SECTION_ID); + if ( !empty($block['category']) ) { + $tweak->setParentId(self::PARENT_PREFIX . $block['category']); + } + $manager->addTweak($tweak); + } + } + + /** @noinspection PhpUnused Actually used as a tweak callback. */ + public function flagBlockAsHidden($blockId) { + $this->hiddenBlocks[] = $blockId; + } + + public function filterAllowedBlocks($allowedBlocks) { + if ( empty($this->hiddenBlocks) ) { + return $allowedBlocks; + } + + if ( $allowedBlocks === true ) { + //All blocks are allowed by default. We need to turn our blacklist into a whitelist. + //Unfortunately, we can't get all available blocks via PHP, so we rely on the cached + //list of registered blocks that was supplied by our JS script. + $registeredBlocks = array_keys(ameUtils::get($this->getDetectedItems(), 'blocks', array())); + $result = array_diff($registeredBlocks, $this->hiddenBlocks); + + //Reindex the array. array_diff() can create "holes" in the array, which means that + //json_encode() will encode it as an object with numeric keys and not a real array. + //As of WP 5.4-alpha, Gutenberg requires a plain array. + return array_values($result); + } else if ( is_array($allowedBlocks) ) { + //Another plugin has already filtered the list of allowed block types. + //Let's remove any blocks that are hidden by AME settings. + return array_values(array_diff($allowedBlocks, $this->hiddenBlocks)); + } + //Either all blocks were hidden by another plugin, or the data type of $allowedBlocks + //is not recognized. + return $allowedBlocks; + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameHideSelectorTweak.php b/extras/modules/tweaks/ameHideSelectorTweak.php new file mode 100644 index 0000000..98b603e --- /dev/null +++ b/extras/modules/tweaks/ameHideSelectorTweak.php @@ -0,0 +1,45 @@ +selector = $selector; + } + + public function apply($settings = null) { + self::$pendingSelectors[] = $this->selector; + + if (did_action(self::OUTPUT_HOOK)) { + self::outputPendingSelectors(); + } else if (!self::$isOutputHookSet) { + add_action(self::OUTPUT_HOOK, array(__CLASS__, 'outputPendingSelectors')); + self::$isOutputHookSet = true; + } + } + + public static function outputPendingSelectors() { + if (empty(self::$pendingSelectors)) { + return; + } + + $css = sprintf( + '', + implode(',', self::$pendingSelectors) + ); + + echo '', "\n", $css, "\n"; + + self::$pendingSelectors = array(); + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameHideSidebarTweak.php b/extras/modules/tweaks/ameHideSidebarTweak.php new file mode 100644 index 0000000..dea933a --- /dev/null +++ b/extras/modules/tweaks/ameHideSidebarTweak.php @@ -0,0 +1,23 @@ +sidebarId = $sidebar['id']; + parent::__construct( + 'hide-sidebar-' . $this->sidebarId, + esc_html($sidebar['name']) + ); + } + + public function apply($settings = null) { + unregister_sidebar($this->sidebarId); + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameHideSidebarWidgetTweak.php b/extras/modules/tweaks/ameHideSidebarWidgetTweak.php new file mode 100644 index 0000000..e9e85a4 --- /dev/null +++ b/extras/modules/tweaks/ameHideSidebarWidgetTweak.php @@ -0,0 +1,26 @@ +widgetClass = get_class($widget); + $this->widget = $widget; + parent::__construct( + 'hide-sidebar-widget-' . $this->widgetClass, + esc_html($widget->name) + ); + } + + public function apply($settings = null) { + unregister_widget($this->widgetClass); + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/ameTinyMceButtonManager.php b/extras/modules/tweaks/ameTinyMceButtonManager.php new file mode 100644 index 0000000..d1bee52 --- /dev/null +++ b/extras/modules/tweaks/ameTinyMceButtonManager.php @@ -0,0 +1,231 @@ + array('title' => 'More buttons'), + 'wp_add_media' => array('title' => 'Add Media'), + 'formatselect' => array('title' => 'Format dropdown'), + 'alignleft' => array('title' => 'Align left'), + 'aligncenter' => array('title' => 'Align center'), + 'alignright' => array('title' => 'Align right'), + 'alignjustify' => array('title' => 'Justify'), + 'alignnone' => array('title' => 'No alignment'), + 'bold' => array('title' => 'Bold'), + 'italic' => array('title' => 'Italic'), + 'underline' => array('title' => 'Underline'), + 'strikethrough' => array('title' => 'Strikethrough'), + 'subscript' => array('title' => 'Subscript'), + 'superscript' => array('title' => 'Superscript'), + 'outdent' => array('title' => 'Decrease indent'), + 'indent' => array('title' => 'Increase indent'), + 'cut' => array('title' => 'Cut'), + 'copy' => array('title' => 'Copy'), + 'paste' => array('title' => 'Paste'), + 'help' => array('title' => 'Help'), + 'selectall' => array('title' => 'Select all'), + 'visualaid' => array('title' => 'Visual aids'), + 'newdocument' => array('title' => 'New document'), + 'removeformat' => array('title' => 'Clear formatting'), + 'remove' => array('title' => 'Remove'), + 'blockquote' => array('title' => 'Blockquote'), + 'undo' => array('title' => 'Undo'), + 'redo' => array('title' => 'Redo'), + 'fontsizeselect' => array('title' => 'Font Size'), + 'fontselect' => array('title' => 'Font Family'), + 'styleselect' => array('title' => 'Style dropdown'), + 'insert' => array('title' => 'Insert menu'), + 'charmap' => array('title' => 'Special character'), + 'hr' => array('title' => 'Horizontal line'), + 'numlist' => array('title' => 'Numbered list'), + 'bullist' => array('title' => 'Bulleted list'), + 'media' => array('title' => 'Insert/edit media'), + 'pastetext' => array('title' => 'Paste as text'), + 'forecolor' => array('title' => 'Text color'), + 'backcolor' => array('title' => 'Background color'), + 'wp_adv' => array('title' => 'Toolbar Toggle'), + 'wp_more' => array('title' => 'Insert Read More tag'), + 'wp_page' => array('title' => 'Page break'), + 'wp_help' => array('title' => 'Keyboard Shortcuts'), + 'wp_code' => array('title' => 'Code'), + 'link' => array('title' => 'Insert/edit link'), + 'unlink' => array('title' => 'Remove link'), + 'spellchecker' => array('title' => 'Toggle spellchecker'), + ); + + public function __construct() { + add_action('admin_init', array($this, 'toggleButtonDetection')); + + $buttonFilters = array('mce_buttons', 'mce_buttons_2', 'mce_buttons_3', 'mce_buttons_4'); + foreach ($buttonFilters as $filter) { + add_filter($filter, array($this, 'filterButtons'), 9000, 1); + } + + add_action('admin-menu-editor-register_tweaks', array($this, 'registerButtonTweaks'), 10, 1); + } + + public function toggleButtonDetection() { + $this->detectionEnabled = current_user_can('activate_plugins') || current_user_can('edit_others_pages'); + } + + public function filterButtons($buttons) { + //Sanity check: $buttons should be an array or something array-like. + if ( !is_array($buttons) && !($buttons instanceof ArrayAccess) ) { + return $buttons; + } + + if ( $this->detectionEnabled ) { + $this->detectNewButtons($buttons); + } + $buttons = $this->removeHiddenButtons($buttons); + return $buttons; + } + + private function detectNewButtons($buttons) { + $newButtons = array_diff($buttons, $this->getKnownButtonIds()); + if ( !empty($newButtons) ) { + $this->newDetectedButtons = array_merge($this->newDetectedButtons, $newButtons); + if ( !$this->storageHookSet ) { + add_action('shutdown', array($this, 'storeNewButtons')); + $this->storageHookSet = true; + } + } + } + + public function storeNewButtons() { + $newButtons = array_fill_keys($this->newDetectedButtons, time()); + $buttons = array_merge($this->getDetectedButtons(), $newButtons); + + //Filter out built-in buttons. We already know they exist, so there's no need + //to store them in the database. + $buttons = array_diff_key($buttons, $this->getBuiltInButtons()); + $this->saveDetectedButtons($buttons); + + $this->newDetectedButtons = array(); + } + + private function saveDetectedButtons($buttons) { + $this->cachedKnownButtons = null; + + $handle = null; + if ( function_exists('flock') ) { + $handle = @fopen(__FILE__, 'r'); + if ( !$handle ) { + return; + } + $success = @flock($handle, LOCK_EX | LOCK_NB); + if ( !$success ) { + fclose($handle); + return; + } + } + + if ( is_multisite() ) { + update_site_option(self::DETECTED_BUTTON_OPTION, $buttons); + } else { + update_option(self::DETECTED_BUTTON_OPTION, $buttons, 'yes'); + } + + if ( $handle !== null ) { + @flock($handle, LOCK_UN); + fclose($handle); + } + } + + /** + * @param string[] $buttons + * @return string[] + */ + private function removeHiddenButtons($buttons) { + if ( empty($this->hiddenButtons) ) { + return $buttons; + } + return array_diff($buttons, $this->hiddenButtons); + } + + private function getKnownButtons() { + if ( $this->cachedKnownButtons === null ) { + $this->cachedKnownButtons = array_merge($this->getDetectedButtons(), $this->getBuiltInButtons()); + } + return $this->cachedKnownButtons; + } + + /** + * @return string[] + */ + private function getKnownButtonIds() { + return array_keys($this->getKnownButtons()); + } + + /** + * @return array + */ + private function getDetectedButtons() { + if ( is_multisite() ) { + $buttons = get_site_option(self::DETECTED_BUTTON_OPTION, array()); + } else { + $buttons = get_option(self::DETECTED_BUTTON_OPTION, array()); + } + if ( !is_array($buttons) ) { + return array(); + } + return $buttons; + } + + private function getBuiltInButtons() { + return $this->builtInButtons; + } + + /** + * @param ameTweakManager $tweakManager + */ + public function registerButtonTweaks($tweakManager) { + $tweakManager->addSection(self::SECTION_ID, 'Hide TinyMCE Buttons', 150); + $theCallback = array($this, 'flagButtonAsHidden'); + + $buttons = $this->getKnownButtons(); + $buttonTweaks = array(); + foreach ($buttons as $id => $details) { + $label = $id; + if ( isset($details['title']) ) { + $label = sprintf('%s (%s)', $details['title'], $id); + } + + $tweak = new ameDelegatedTweak('hide-tmce-' . $id, $label, $theCallback, array($id)); + $tweak->setSectionId(self::SECTION_ID); + $buttonTweaks[] = $tweak; + } + + //Sort tweaks by label. + uasort( + $buttonTweaks, + /** + * @param ameBaseTweak $a + * @param ameBaseTweak $b + * @return int + */ + function ($a, $b) { + return strnatcasecmp($a->getLabel(), $b->getLabel()); + } + ); + + foreach ($buttonTweaks as $tweak) { + $tweakManager->addTweak($tweak); + } + } + + /** @noinspection PhpUnused Actually used in registerButtonTweaks(). */ + public function flagButtonAsHidden($buttonId) { + $this->hiddenButtons[] = $buttonId; + } +} \ No newline at end of file diff --git a/extras/modules/tweaks/configurables.php b/extras/modules/tweaks/configurables.php new file mode 100644 index 0000000..d5dd132 --- /dev/null +++ b/extras/modules/tweaks/configurables.php @@ -0,0 +1,284 @@ +id = $id; + $this->label = ($label !== null) ? $label : $id; + } + + public function getId() { + return $this->id; + } + + public function getLabel() { + return $this->label; + } + + public function toArray() { + $result = array( + 'id' => $this->getId(), + 'label' => $this->getLabel(), + ); + + if ( $this instanceof IterableSettingsCollection ) { + $children = []; + foreach ($this as $item) { + $children[] = $item->toArray(); + } + $result['children'] = $children; + } + + return $result; + } +} + +class SettingsGroup extends NamedNode implements IterableSettingsCollection { + const ID_AS_PATH = '.'; + + /** + * @var NamedNode[] + */ + protected $items = []; + + /** + * @var string|null + */ + protected $propertyPath = null; + + /** + * SettingsGroup constructor. + * + * @param string $id + * @param string|null $label + * @param string|null $propertyPath + */ + public function __construct($id, $label = null, $propertyPath = self::ID_AS_PATH) { + parent::__construct($id, $label); + if ( $propertyPath === self::ID_AS_PATH ) { + $this->propertyPath = $this->id; + } else { + $this->propertyPath = $propertyPath; + } + } + + #[\ReturnTypeWillChange] + public function getIterator() { + return new ArrayIterator($this->items); + } + + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + return array_key_exists($offset, $this->items); + } + + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->items[$offset]; + } + + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + if ( !($value instanceof NamedNode) ) { + throw new InvalidArgumentException( + 'Tried to add an invalid item to ' . __CLASS__ . '. Expected a NamedNode.' + ); + } + $this->items[$offset] = $value; + } + + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + unset($this->items[$offset]); + } + + public function toArray() { + $result = parent::toArray(); + $result['type'] = 'group'; + $result['propertyPath'] = $this->propertyPath; + return $result; + } + + public function add(NamedNode $child) { + $id = $child->getId(); + if ( ($id !== '') && ($id !== null) ) { + $this->items[$child->getId()] = $child; + } else { + $this->items[] = $child; + } + return $this; + } + + public function hasAnySettings() { + if ( empty($this->items) ) { + return false; + } + foreach ($this->items as $item) { + if ( $item instanceof Setting ) { + return true; + } else if ( ($item instanceof IterableSettingsCollection) && $item->hasAnySettings() ) { + return true; + } + } + return false; + } +} + +class ActorFeature extends NamedNode implements IterableSettingsCollection { + /** + * @var SettingsGroup + */ + protected $children; + protected $defaultAccessMap = null; + + public function __construct($id, $label = null) { + parent::__construct($id, $label); + $this->children = new SettingsGroup('', '', null); + } + + /** + * @param array |null $accessMap + * @return $this + */ + public function setDefaultAccessMap($accessMap) { + $this->defaultAccessMap = $accessMap; + return $this; + } + + #[\ReturnTypeWillChange] + public function getIterator() { + return $this->children->getIterator(); + } + + #[\ReturnTypeWillChange] + public function offsetExists($offset) { + return $this->children->offsetExists($offset); + } + + #[\ReturnTypeWillChange] + public function offsetGet($offset) { + return $this->children[$offset]; + } + + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) { + $this->children[$offset] = $value; + } + + #[\ReturnTypeWillChange] + public function offsetUnset($offset) { + unset($this->children[$offset]); + } + + public function add(NamedNode $child) { + $this->children->add($child); + return $this; + } + + public function toArray() { + $result = parent::toArray(); + $result['hasAccessMap'] = true; + if ( $this->defaultAccessMap !== null ) { + $result['defaultAccessMap'] = $this->defaultAccessMap; + } + return $result; + } + + public function hasAnySettings() { + return $this->children->hasAnySettings(); + } +} + +class Setting extends NamedNode { + /** + * @var mixed|null + */ + public $defaultValue = null; + + /** + * @var string + */ + protected $dataType = 'null'; + + /** + * @var string|null + */ + protected $inputType = null; + + public function toArray() { + $result = parent::toArray(); + $result['dataType'] = $this->dataType; + $result['inputType'] = $this->inputType; + if ( $this->defaultValue !== null ) { + $result['defaultValue'] = $this->defaultValue; + } + return $result; + } +} + +class StringSetting extends Setting { + protected $dataType = 'string'; + protected $inputType = 'text'; + + public $syntaxHighlighting = null; + + public function textarea($syntaxHighlighting = null) { + $this->inputType = 'textarea'; + $this->syntaxHighlighting = $syntaxHighlighting; + return $this; + } + + public function toArray() { + $result = parent::toArray(); + if ( $this->syntaxHighlighting !== null ) { + $result['syntaxHighlighting'] = $this->syntaxHighlighting; + } + return $result; + } +} + +class ColorSetting extends Setting { + protected $dataType = 'string'; + protected $inputType = 'color'; +} + +class BooleanSetting extends Setting { + protected $dataType = 'boolean'; +} \ No newline at end of file diff --git a/extras/modules/tweaks/default-tweaks.php b/extras/modules/tweaks/default-tweaks.php new file mode 100644 index 0000000..4ef6032 --- /dev/null +++ b/extras/modules/tweaks/default-tweaks.php @@ -0,0 +1,85 @@ + array( + 'profile' => array('label' => 'Hide Profile Fields', 'priority' => 80), + 'sidebar-widgets' => array('label' => 'Hide Sidebar Widgets', 'priority' => 100), + 'sidebars' => array('label' => 'Hide Sidebars', 'priority' => 120), + 'environment-type' => array('label' => 'Environment Type', 'priority' => 30), + ), + + 'tweaks' => array( + 'hide-screen-meta-links' => array( + 'label' => 'Hide screen meta links', + 'selector' => '#screen-meta-links', + 'hideableLabel' => 'Screen meta links', + 'hideableCategory' => 'admin-ui', + ), + 'hide-screen-options' => array( + 'label' => 'Hide the "Screen Options" button', + 'selector' => '#screen-options-link-wrap', + 'parent' => 'hide-screen-meta-links', + 'hideableLabel' => '"Screen Options" button', + 'hideableCategory' => 'admin-ui', + ), + 'hide-help-panel' => array( + 'label' => 'Hide the "Help" button', + 'selector' => '#contextual-help-link-wrap', + 'parent' => 'hide-screen-meta-links', + 'hideableLabel' => '"Help" button', + 'hideableCategory' => 'admin-ui', + ), + 'hide-all-admin-notices' => array( + 'label' => 'Hide ALL admin notices', + 'selector' => '#wpbody-content .notice, #wpbody-content .updated, #wpbody-content .update-nag', + 'hideableLabel' => 'All admin notices', + 'hideableCategory' => 'admin-ui', + ), + + 'hide-gutenberg-options' => array( + 'label' => 'Hide the Gutenberg options menu (three vertical dots)', + 'selector' => '#editor .edit-post-header__settings .edit-post-more-menu', + ), + 'hide-gutenberg-fs-wp-logo' => array( + 'label' => 'Hide the WordPress logo in Gutenberg fullscreen mode', + 'selector' => '#editor .edit-post-header a.components-button[href^="edit.php"]', + ), + + 'hide-profile-visual-editor' => array( + 'label' => 'Visual Editor', + 'selector' => 'tr.user-rich-editing-wrap', + 'section' => 'profile', + 'screens' => array('profile'), + ), + 'hide-profile-syntax-higlighting' => array( + 'label' => 'Syntax Highlighting', + 'selector' => 'tr.user-syntax-highlighting-wrap', + 'section' => 'profile', + 'screens' => array('profile'), + ), + 'hide-profile-color-scheme-selector' => array( + 'label' => 'Admin Color Scheme', + 'selector' => 'tr.user-admin-color-wrap', + 'section' => 'profile', + 'screens' => array('profile'), + ), + 'hide-profile-toolbar-toggle' => array( + 'label' => 'Toolbar', + 'selector' => 'tr.show-admin-bar.user-admin-bar-front-wrap', + 'section' => 'profile', + 'screens' => array('profile'), + ), + + 'show-environment-in-toolbar' => array( + 'label' => 'Show environment type in the Toolbar', + 'section' => 'environment-type', + 'className' => 'ameEnvironmentNameTweak', + 'includeFile' => __DIR__ . '/ameEnvironmentNameTweak.php', + ), + 'environment-dependent-colors' => array( + 'label' => 'Change menu color depending on the environment', + 'section' => 'environment-type', + 'className' => 'ameEnvironmentColorTweak', + 'includeFile' => __DIR__ . '/ameEnvironmentColorTweak.php', + ), + ), +); \ No newline at end of file diff --git a/extras/modules/tweaks/gutenberg-block-detector.js b/extras/modules/tweaks/gutenberg-block-detector.js new file mode 100644 index 0000000..74af337 --- /dev/null +++ b/extras/modules/tweaks/gutenberg-block-detector.js @@ -0,0 +1,84 @@ +/** + * @property {Object} window.wsAmeGutenbergBlockData + * @property wp.domReady + * @property wp.blocks + * @property wp.blocks.getBlockTypes + * @property wp.blocks.getCategories + * @property window._wpLoadBlockEditor + * @property window._wpLoadGutenbergEditor + */ + +if (typeof wp !== 'undefined' && typeof wp.domReady !== 'undefined') { + wp.domReady(function () { + let loadGutenberg = null; + if (typeof window._wpLoadBlockEditor !== 'undefined') { + loadGutenberg = window._wpLoadBlockEditor; + } else if (typeof window._wpLoadGutenbergEditor !== 'undefined') { + loadGutenberg = window._wpLoadGutenbergEditor; + } + + if ((loadGutenberg === null) || (typeof loadGutenberg.then === 'undefined')) { + return; + } + + const scriptData = (typeof window.wsAmeGutenbergBlockData !== 'undefined') + ? window.wsAmeGutenbergBlockData + : null; + + //We must have the AJAX URL and an update nonce to save detected blocks. + //If we don't have those, this script can't do anything. + if (!scriptData) { + return; + } + + //Wait for Gutenberg to load. + loadGutenberg.then(function () { + setTimeout(function () { + let hasNewData = false; + + //We're using arrays instead of objects because we want to preserve item order. + let registeredBlocks = []; + const blocks = wp.blocks.getBlockTypes(); + for (let i = 0; i < blocks.length; i++) { + const block = blocks[i]; + registeredBlocks.push({ + name: block.name, + title: block.title, + category: block.category + }); + + if (!scriptData.knownBlocks.hasOwnProperty(block.name)) { + hasNewData = true; + } + } + + let registeredCategories = [], + categories = wp.blocks.getCategories(); + for (let j = 0; j < categories.length; j++) { + registeredCategories.push({ + slug: categories[j].slug, + title: categories[j].title, + }); + + if (!scriptData.knownCategories.hasOwnProperty(categories[j].slug)) { + hasNewData = true; + } + } + + if (hasNewData && scriptData.updateNonce && scriptData.ajaxAction) { + //Save the registered blocks and categories. + jQuery.post( + scriptData.ajaxUrl, + { + action: scriptData.ajaxAction, + _ajax_nonce: scriptData.updateNonce, + blocks: JSON.stringify(registeredBlocks), + categories: JSON.stringify(registeredCategories) + } + ); + } + }, 50); + }); + + }); +} \ No newline at end of file diff --git a/extras/modules/tweaks/tweak-manager.js b/extras/modules/tweaks/tweak-manager.js new file mode 100644 index 0000000..77c3a2c --- /dev/null +++ b/extras/modules/tweaks/tweak-manager.js @@ -0,0 +1,791 @@ +/// +/// +/// +/// +/// +/// +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var AmeNamedNode = /** @class */ (function () { + function AmeNamedNode(properties) { + this.htmlId = ''; + this.id = properties.id; + this.label = properties.label; + } + return AmeNamedNode; +}()); +function isAmeSettingsGroupProperties(thing) { + var group = thing; + return (typeof group.children !== 'undefined'); +} +function isAmeSettingProperties(thing) { + return (typeof thing.dataType === 'string'); +} +var AmeSetting = /** @class */ (function (_super) { + __extends(AmeSetting, _super); + function AmeSetting(properties, store, path) { + if (path === void 0) { path = []; } + var _this = _super.call(this, properties) || this; + var defaultValue = null; + if (typeof properties.defaultValue !== 'undefined') { + defaultValue = properties.defaultValue; + } + _this.inputValue = store.getObservableProperty(properties.id, defaultValue, path); + AmeSetting.idCounter++; + _this.uniqueInputId = 'ws-ame-gen-setting-' + AmeSetting.idCounter; + return _this; + } + AmeSetting.idCounter = 0; + return AmeSetting; +}(AmeNamedNode)); +var AmeStringSetting = /** @class */ (function (_super) { + __extends(AmeStringSetting, _super); + function AmeStringSetting(properties, module, store, path) { + if (path === void 0) { path = []; } + var _this = _super.call(this, properties, store, path) || this; + _this.syntaxHighlightingOptions = null; + _this.templateName = 'ame-tweak-textarea-input-template'; + if (properties.syntaxHighlighting && module) { + _this.syntaxHighlightingOptions = module.getCodeMirrorOptions(properties.syntaxHighlighting); + } + return _this; + } + return AmeStringSetting; +}(AmeSetting)); +var AmeColorSetting = /** @class */ (function (_super) { + __extends(AmeColorSetting, _super); + function AmeColorSetting(properties, store, path) { + if (path === void 0) { path = []; } + var _this = _super.call(this, properties, store, path) || this; + _this.templateName = 'ame-tweak-color-input-template'; + return _this; + } + return AmeColorSetting; +}(AmeSetting)); +var AmeBooleanSetting = /** @class */ (function (_super) { + __extends(AmeBooleanSetting, _super); + function AmeBooleanSetting(properties, store, path) { + if (path === void 0) { path = []; } + var _this = _super.call(this, properties, store, path) || this; + _this.templateName = 'ame-tweak-boolean-input-template'; + //Ensure that the value is always a boolean. + var _internalValue = _this.inputValue; + if (typeof _internalValue() !== 'boolean') { + _internalValue(!!_internalValue()); + } + _this.inputValue = ko.computed({ + read: function () { + return _internalValue(); + }, + write: function (newValue) { + if (typeof newValue !== 'boolean') { + newValue = !!newValue; + } + _internalValue(newValue); + }, + owner: _this + }); + return _this; + } + return AmeBooleanSetting; +}(AmeSetting)); +function isAmeActorFeatureProperties(thing) { + return (typeof thing.hasAccessMap === 'boolean'); +} +var AmeSettingStore = /** @class */ (function () { + function AmeSettingStore(initialProperties) { + if (initialProperties === void 0) { initialProperties = {}; } + this.observableProperties = {}; + this.accessMaps = {}; + this.initialProperties = initialProperties; + } + AmeSettingStore.prototype.getObservableProperty = function (name, defaultValue, path) { + if (path === void 0) { path = []; } + path = this.getFullPath(name, path); + if (this.observableProperties.hasOwnProperty(path)) { + return this.observableProperties[path]; + } + var _ = AmeTweakManagerModule._; + var value = _.get(this.initialProperties, path, defaultValue); + var observable = ko.observable(value); + this.observableProperties[path] = observable; + return observable; + }; + AmeSettingStore.prototype.getFullPath = function (name, path) { + if (typeof path !== 'string') { + path = path.join('.'); + } + if (path === '') { + path = name; + } + else { + path = path + '.' + name; + } + return path; + }; + AmeSettingStore.prototype.propertiesToJs = function () { + var _ = AmeTweakManagerModule._; + var newProps = {}; + _.forOwn(this.observableProperties, function (observable, path) { + _.set(newProps, path, observable()); + }); + _.forOwn(this.accessMaps, function (map, path) { + //Since all tweaks are disabled by default, having a tweak disabled for a role is the same + //as not having a setting, so we can save some space by removing it. This does not always + //apply to users/Super Admins because they can have precedence over roles. + var temp = map.getAll(); + var enabled = {}; + var areAllFalse = true; + for (var actorId in temp) { + if (!temp.hasOwnProperty(actorId)) { + continue; + } + areAllFalse = areAllFalse && (!temp[actorId]); + if (!temp[actorId]) { + var actor = AmeActors.getActor(actorId); + if (actor instanceof AmeRole) { + continue; + } + } + enabled[actorId] = temp[actorId]; + } + if (areAllFalse) { + enabled = {}; + } + _.set(newProps, path, enabled); + }); + return newProps; + }; + AmeSettingStore.prototype.getAccessMap = function (name, path, defaultAccessMap) { + if (path === void 0) { path = []; } + if (defaultAccessMap === void 0) { defaultAccessMap = null; } + path = this.getFullPath(name, path); + var _ = AmeTweakManagerModule._; + var value = _.get(this.initialProperties, path, defaultAccessMap); + if (!this.accessMaps.hasOwnProperty(path)) { + this.accessMaps[path] = new AmeObservableActorSettings(value); + } + return this.accessMaps[path]; + }; + return AmeSettingStore; +}()); +function isSettingStore(thing) { + var maybe = thing; + return (typeof maybe.getObservableProperty !== 'undefined') && (typeof maybe.propertiesToJs !== 'undefined'); +} +var AmeCompositeNode = /** @class */ (function (_super) { + __extends(AmeCompositeNode, _super); + function AmeCompositeNode(properties, module, store, path) { + if (store === void 0) { store = null; } + if (path === void 0) { path = []; } + var _this = _super.call(this, properties) || this; + _this.children = null; + _this.id = properties.id; + _this.label = properties.label; + if (store === 'self') { + if (!_this.properties) { + _this.properties = new AmeSettingStore(properties); + } + store = _this.properties; + } + if (isAmeSettingsGroupProperties(properties)) { + if ((typeof properties.propertyPath === 'string') && (properties.propertyPath !== '')) { + _this.propertyPath = properties.propertyPath.split('.'); + } + else { + _this.propertyPath = []; + } + if (path.length > 0) { + _this.propertyPath = path.concat(_this.propertyPath); + } + var children = []; + if (properties.children && (properties.children.length > 0)) { + for (var i = 0; i < properties.children.length; i++) { + var props = properties.children[i]; + var child = void 0; + if (isAmeSettingProperties(props)) { + child = AmeCompositeNode.createSetting(props, module, store, _this.propertyPath); + } + else { + child = new AmeCompositeNode(props, module, store, _this.propertyPath); + } + if (child) { + children.push(child); + } + } + } + _this.children = ko.observableArray(children); + } + if (isAmeActorFeatureProperties(properties)) { + var name_1 = (store === _this.properties) ? 'enabledForActor' : _this.id; + var defaultAccess = (typeof properties.defaultAccessMap !== 'undefined') ? properties.defaultAccessMap : null; + _this.actorAccess = new AmeActorAccess(store.getAccessMap(name_1, path, defaultAccess), module, _this.children); + } + return _this; + } + AmeCompositeNode.createSetting = function (properties, module, store, path) { + if (path === void 0) { path = []; } + var inputType = properties.inputType ? properties.inputType : properties.dataType; + switch (inputType) { + case 'text': + case 'textarea': + case 'string': + return new AmeStringSetting(properties, module, store, path); + case 'color': + return new AmeColorSetting(properties, store, path); + case 'boolean': + return new AmeBooleanSetting(properties, store, path); + default: + if (console && console.error) { + console.error('Unknown setting input type "%s"', inputType); + } + return null; + } + }; + return AmeCompositeNode; +}(AmeNamedNode)); +var AmeActorAccess = /** @class */ (function () { + function AmeActorAccess(actorSettings, module, children) { + if (children === void 0) { children = null; } + var _this = this; + this.module = module; + this.enabledForActor = actorSettings; + var _isIndeterminate = ko.observable(false); + this.isIndeterminate = ko.computed(function () { + if (module.selectedActor() !== null) { + return false; + } + return _isIndeterminate(); + }); + this.isChecked = ko.computed({ + read: function () { + var selectedActor = _this.module.selectedActor(); + if (selectedActor === null) { + //All: Checked only if it's checked for all actors. + var allActors = _this.module.actorSelector.getVisibleActors(); + var isEnabledForAll = true, isEnabledForAny = false; + for (var index = 0; index < allActors.length; index++) { + if (_this.enabledForActor.get(allActors[index].getId(), false)) { + isEnabledForAny = true; + } + else { + isEnabledForAll = false; + } + } + _isIndeterminate(isEnabledForAny && !isEnabledForAll); + return isEnabledForAll; + } + //Is there an explicit setting for this actor? + var ownSetting = _this.enabledForActor.get(selectedActor.getId(), null); + if (ownSetting !== null) { + return ownSetting; + } + if (selectedActor instanceof AmeUser) { + //The "Super Admin" setting takes precedence over regular roles. + if (selectedActor.isSuperAdmin) { + var superAdminSetting = _this.enabledForActor.get(AmeSuperAdmin.permanentActorId, null); + if (superAdminSetting !== null) { + return superAdminSetting; + } + } + //Is it enabled for any of the user's roles? + for (var i = 0; i < selectedActor.roles.length; i++) { + var groupSetting = _this.enabledForActor.get('role:' + selectedActor.roles[i], null); + if (groupSetting === true) { + return true; + } + } + } + //All tweaks are unchecked by default. + return false; + }, + write: function (checked) { + var selectedActor = _this.module.selectedActor(); + if (selectedActor === null) { + //Enable/disable this tweak for all actors. + if (checked === false) { + //Since false is the default, this is the same as removing/resetting all values. + _this.enabledForActor.resetAll(); + } + else { + var allActors = _this.module.actorSelector.getVisibleActors(); + for (var i = 0; i < allActors.length; i++) { + _this.enabledForActor.set(allActors[i].getId(), checked); + } + } + } + else { + _this.enabledForActor.set(selectedActor.getId(), checked); + } + //Apply the same setting to all children. + if (children) { + var childrenArray = children(); + for (var i = 0; i < childrenArray.length; i++) { + var child = childrenArray[i]; + if ((child instanceof AmeCompositeNode) && child.actorAccess) { + child.actorAccess.isChecked(checked); + } + } + } + } + }); + } + return AmeActorAccess; +}()); +var AmeTweakItem = /** @class */ (function (_super) { + __extends(AmeTweakItem, _super); + function AmeTweakItem(properties, module) { + var _this = _super.call(this, properties, module, 'self') || this; + _this.initialProperties = null; + _this.section = null; + _this.parent = null; + _this.isUserDefined = properties.isUserDefined ? properties.isUserDefined : false; + if (_this.isUserDefined) { + _this.initialProperties = properties; + } + if (_this.isUserDefined) { + _this.label = ko.observable(properties.label); + } + else { + _this.label = ko.pureComputed(function () { + return properties.label; + }); + } + _this.htmlId = 'ame-tweak-' + AmeTweakManagerModule.slugify(_this.id); + return _this; + } + AmeTweakItem.prototype.toJs = function () { + var result = { + id: this.id + }; + var _ = AmeTweakManagerModule._; + if (this.properties) { + result = _.defaults(result, this.properties.propertiesToJs()); + } + if (!this.isUserDefined) { + return result; + } + else { + var props = result; + props.isUserDefined = this.isUserDefined; + props.label = this.label(); + props.sectionId = this.section ? this.section.id : null; + props.parentId = this.parent ? this.parent.id : null; + props = _.defaults(props, _.omit(this.initialProperties, 'userInputValue', 'enabledForActor')); + return props; + } + }; + AmeTweakItem.prototype.setSection = function (section) { + this.section = section; + return this; + }; + AmeTweakItem.prototype.setParent = function (tweak) { + this.parent = tweak; + return this; + }; + AmeTweakItem.prototype.getSection = function () { + return this.section; + }; + AmeTweakItem.prototype.getParent = function () { + return this.parent; + }; + AmeTweakItem.prototype.addChild = function (tweak) { + this.children.push(tweak); + tweak.setParent(this); + return this; + }; + AmeTweakItem.prototype.removeChild = function (tweak) { + this.children.remove(tweak); + }; + AmeTweakItem.prototype.getEditableProperty = function (key) { + if (this.properties) { + return this.properties.getObservableProperty(key, ''); + } + }; + AmeTweakItem.prototype.getTypeId = function () { + if (!this.isUserDefined || !this.initialProperties) { + return null; + } + if (this.initialProperties.typeId) { + return this.initialProperties.typeId; + } + return null; + }; + return AmeTweakItem; +}(AmeCompositeNode)); +var AmeTweakSection = /** @class */ (function () { + function AmeTweakSection(properties) { + this.footerTemplateName = null; + this.id = properties.id; + this.label = properties.label; + this.isOpen = ko.observable(true); + this.tweaks = ko.observableArray([]); + } + AmeTweakSection.prototype.addTweak = function (tweak) { + this.tweaks.push(tweak); + tweak.setSection(this); + }; + AmeTweakSection.prototype.removeTweak = function (tweak) { + this.tweaks.remove(tweak); + }; + AmeTweakSection.prototype.hasContent = function () { + return this.tweaks().length > 0; + }; + AmeTweakSection.prototype.toggle = function () { + this.isOpen(!this.isOpen()); + }; + return AmeTweakSection; +}()); +var AmeTweakManagerModule = /** @class */ (function () { + function AmeTweakManagerModule(scriptData) { + var _this = this; + this.tweaksById = {}; + this.sectionsById = {}; + this.sections = []; + this.lastUserTweakSuffix = 0; + var _ = AmeTweakManagerModule._; + this.actorSelector = new AmeActorSelector(AmeActors, scriptData.isProVersion); + this.selectedActorId = this.actorSelector.createKnockoutObservable(ko); + this.selectedActor = ko.computed(function () { + var id = _this.selectedActorId(); + if (id === null) { + return null; + } + return AmeActors.getActor(id); + }); + //Reselect the previously selected actor. + this.selectedActorId(scriptData.selectedActor); + //Set syntax highlighting options. + this.cssHighlightingOptions = _.merge({}, scriptData.defaultCodeEditorSettings, { + 'codemirror': { + 'mode': 'css', + 'lint': true, + 'autoCloseBrackets': true, + 'matchBrackets': true + } + }); + //Sort sections by priority, then by label. + var sectionData = _.sortByAll(scriptData.sections, ['priority', 'label']); + //Register sections. + _.forEach(sectionData, function (properties) { + var section = new AmeTweakSection(properties); + _this.sectionsById[section.id] = section; + _this.sections.push(section); + }); + var firstSection = this.sections[0]; + _.forEach(scriptData.tweaks, function (properties) { + var tweak = new AmeTweakItem(properties, _this); + _this.tweaksById[tweak.id] = tweak; + if (properties.parentId && _this.tweaksById.hasOwnProperty(properties.parentId)) { + _this.tweaksById[properties.parentId].addChild(tweak); + } + else { + var ownerSection = firstSection; + if (properties.sectionId && _this.sectionsById.hasOwnProperty(properties.sectionId)) { + ownerSection = _this.sectionsById[properties.sectionId]; + } + ownerSection.addTweak(tweak); + } + }); + //Remove empty sections. + this.sections = _.filter(this.sections, function (section) { + return section.hasContent(); + }); + //Add the tweak creation button to the Admin CSS section. + if (this.sectionsById.hasOwnProperty('admin-css')) { + this.sectionsById['admin-css'].footerTemplateName = 'ame-admin-css-section-footer'; + } + //By default, all sections except the first one are closed. + //The user can open/close sections and we automatically remember their state. + this.openSectionIds = ko.computed({ + read: function () { + var result = []; + _.forEach(_this.sections, function (section) { + if (section.isOpen()) { + result.push(section.id); + } + }); + return result; + }, + write: function (sectionIds) { + var openSections = _.indexBy(sectionIds); + _.forEach(_this.sections, function (section) { + section.isOpen(openSections.hasOwnProperty(section.id)); + }); + } + }); + this.openSectionIds.extend({ rateLimit: { timeout: 1000, method: 'notifyWhenChangesStop' } }); + var initialState = null; + var cookieValue = jQuery.cookie(AmeTweakManagerModule.openSectionCookieName); + if ((typeof cookieValue === 'string') && JSON && JSON.parse) { + var storedState = JSON.parse(cookieValue); + if (_.isArray(storedState)) { + initialState = _.intersection(_.keys(this.sectionsById), storedState); + } + } + if (initialState !== null) { + this.openSectionIds(initialState); + } + else { + this.openSectionIds([_.first(this.sections).id]); + } + this.openSectionIds.subscribe(function (sectionIds) { + jQuery.cookie(AmeTweakManagerModule.openSectionCookieName, ko.toJSON(sectionIds), { expires: 90 }); + }); + if (scriptData.lastUserTweakSuffix) { + this.lastUserTweakSuffix = scriptData.lastUserTweakSuffix; + } + this.adminCssEditorDialog = new AmeEditAdminCssDialog(this); + this.settingsData = ko.observable(''); + this.isSaving = ko.observable(false); + } + AmeTweakManagerModule.prototype.saveChanges = function () { + this.isSaving(true); + var _ = wsAmeLodash; + var data = { + 'tweaks': _.indexBy(_.invoke(this.tweaksById, 'toJs'), 'id'), + 'lastUserTweakSuffix': this.lastUserTweakSuffix + }; + this.settingsData(ko.toJSON(data)); + return true; + }; + AmeTweakManagerModule.prototype.addAdminCssTweak = function (label, css) { + this.lastUserTweakSuffix++; + var slug = AmeTweakManagerModule.slugify(label); + if (slug !== '') { + slug = '-' + slug; + } + var props = { + label: label, + id: 'utw-' + this.lastUserTweakSuffix + slug, + isUserDefined: true, + sectionId: 'admin-css', + typeId: 'admin-css', + children: [], + hasAccessMap: true + }; + props['css'] = css; + var cssInput = { + id: 'css', + label: '', + dataType: 'string', + inputType: 'textarea', + syntaxHighlighting: 'css' + }; + props.children.push(cssInput); + var newTweak = new AmeTweakItem(props, this); + this.tweaksById[newTweak.id] = newTweak; + this.sectionsById['admin-css'].addTweak(newTweak); + }; + AmeTweakManagerModule.slugify = function (input) { + var _ = AmeTweakManagerModule._; + var output = _.deburr(input); + output = output.replace(/[^a-zA-Z0-9 \-]/, ''); + return _.kebabCase(output); + }; + AmeTweakManagerModule.prototype.launchTweakEditor = function (tweak) { + // noinspection JSRedundantSwitchStatement + switch (tweak.getTypeId()) { + case 'admin-css': + this.adminCssEditorDialog.selectedTweak = tweak; + this.adminCssEditorDialog.open(); + break; + default: + alert('Error: Editor not implemented! This is probably a bug.'); + } + }; + AmeTweakManagerModule.prototype.confirmDeleteTweak = function (tweak) { + if (!tweak.isUserDefined || !confirm('Delete this tweak?')) { + return; + } + this.deleteTweak(tweak); + }; + AmeTweakManagerModule.prototype.deleteTweak = function (tweak) { + var section = tweak.getSection(); + if (section) { + section.removeTweak(tweak); + } + var parent = tweak.getParent(); + if (parent) { + parent.removeChild(tweak); + } + delete this.tweaksById[tweak.id]; + }; + AmeTweakManagerModule.prototype.getCodeMirrorOptions = function (mode) { + if (mode === 'css') { + return this.cssHighlightingOptions; + } + return null; + }; + AmeTweakManagerModule._ = wsAmeLodash; + AmeTweakManagerModule.openSectionCookieName = 'ame_tmce_open_sections'; + return AmeTweakManagerModule; +}()); +var AmeEditAdminCssDialog = /** @class */ (function () { + function AmeEditAdminCssDialog(manager) { + var _this = this; + this.autoCancelButton = false; + this.options = { + minWidth: 400 + }; + this.selectedTweak = null; + var _ = AmeTweakManagerModule._; + this.manager = manager; + this.tweakLabel = ko.observable(''); + this.cssCode = ko.observable(''); + this.confirmButtonText = ko.observable('Add Snippet'); + this.title = ko.observable(null); + this.isAddButtonEnabled = ko.computed(function () { + return !((_.trim(_this.tweakLabel()) === '') || (_.trim(_this.cssCode()) === '')); + }); + this.isOpen = ko.observable(false); + } + AmeEditAdminCssDialog.prototype.onOpen = function (event, ui) { + if (this.selectedTweak) { + this.tweakLabel(this.selectedTweak.label()); + this.title('Edit admin CSS snippet'); + this.confirmButtonText('Save Changes'); + var cssProperty = this.selectedTweak.getEditableProperty('css'); + this.cssCode(cssProperty ? cssProperty() : ''); + } + else { + this.tweakLabel(''); + this.cssCode(''); + this.title('Add admin CSS snippet'); + this.confirmButtonText('Add Snippet'); + } + }; + AmeEditAdminCssDialog.prototype.onConfirm = function () { + if (this.selectedTweak) { + //Update the existing tweak. + this.selectedTweak.label(this.tweakLabel()); + this.selectedTweak.getEditableProperty('css')(this.cssCode()); + } + else { + //Create a new tweak. + this.manager.addAdminCssTweak(this.tweakLabel(), this.cssCode()); + } + this.close(); + }; + AmeEditAdminCssDialog.prototype.onClose = function () { + this.selectedTweak = null; + }; + AmeEditAdminCssDialog.prototype.close = function () { + this.isOpen(false); + }; + AmeEditAdminCssDialog.prototype.open = function () { + this.isOpen(true); + }; + return AmeEditAdminCssDialog; +}()); +ko.bindingHandlers.ameCodeMirror = { + init: function (element, valueAccessor, allBindings) { + if (!wp.hasOwnProperty('codeEditor') || !wp.codeEditor.initialize) { + return; + } + var parameters = ko.unwrap(valueAccessor()); + if (!parameters) { + return; + } + var options; + var refreshTrigger; + if (parameters.options) { + options = parameters.options; + if (parameters.refreshTrigger) { + refreshTrigger = parameters.refreshTrigger; + } + } + else { + options = parameters; + } + var result = wp.codeEditor.initialize(element, options); + var cm = result.codemirror; + //Synchronise the editor contents with the observable passed to the "value" binding. + var valueObservable = allBindings.get('value'); + if (!ko.isObservable(valueObservable)) { + valueObservable = null; + } + var subscription = null; + var changeHandler = null; + if (valueObservable !== null) { + //Update the observable when the contents of the editor change. + var ignoreNextUpdate_1 = false; + changeHandler = function () { + //This will trigger our observable subscription (see below). + //We need to ignore that trigger to avoid recursive or duplicated updates. + ignoreNextUpdate_1 = true; + valueObservable(cm.doc.getValue()); + }; + cm.on('changes', changeHandler); + //Update the editor when the observable changes. + subscription = valueObservable.subscribe(function (newValue) { + if (ignoreNextUpdate_1) { + ignoreNextUpdate_1 = false; + return; + } + cm.doc.setValue(newValue); + ignoreNextUpdate_1 = false; + }); + } + //Refresh the size of the editor element when an observable changes value. + var refreshSubscription = null; + if (refreshTrigger) { + refreshSubscription = refreshTrigger.subscribe(function () { + cm.refresh(); + }); + } + //If the editor starts out hidden - for example, because it's inside a collapsed section - it will + //render incorrectly. To fix that, let's refresh it the first time it becomes visible. + if (!jQuery(element).is(':visible') && (typeof IntersectionObserver !== 'undefined')) { + var observer_1 = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + if (entries[i].isIntersecting) { + //The editor is at least partially visible now. + observer_1.disconnect(); + cm.refresh(); + break; + } + } + }, { + //Use the browser viewport. + root: null, + //The threshold is somewhat arbitrary. Any value will work, but a lower setting means + //that the user is less likely to see an incorrectly rendered editor. + threshold: 0.05 + }); + observer_1.observe(cm.getWrapperElement()); + } + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + //Remove subscriptions and event handlers. + if (subscription) { + subscription.dispose(); + } + if (refreshSubscription) { + refreshSubscription.dispose(); + } + if (changeHandler) { + cm.off('changes', changeHandler); + } + //Destroy the CodeMirror instance. + jQuery(cm.getWrapperElement()).remove(); + }); + } +}; +jQuery(function () { + ameTweakManager = new AmeTweakManagerModule(wsTweakManagerData); + ko.applyBindings(ameTweakManager, document.getElementById('ame-tweak-manager')); +}); +//# sourceMappingURL=tweak-manager.js.map \ No newline at end of file diff --git a/extras/modules/tweaks/tweak-manager.js.map b/extras/modules/tweaks/tweak-manager.js.map new file mode 100644 index 0000000..87cb76c --- /dev/null +++ b/extras/modules/tweaks/tweak-manager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tweak-manager.js","sourceRoot":"","sources":["tweak-manager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/extras/modules/tweaks/tweak-manager.ts b/extras/modules/tweaks/tweak-manager.ts new file mode 100644 index 0000000..64feb72 --- /dev/null +++ b/extras/modules/tweaks/tweak-manager.ts @@ -0,0 +1,1016 @@ +/// +/// +/// +/// +/// +/// + +declare let ameTweakManager: AmeTweakManagerModule; +declare const wsTweakManagerData: AmeTweakManagerScriptData; + +declare const wp: { + codeEditor: { + initialize: (textarea: string, options: object) => any; + }; +}; + +interface AmeTweakManagerScriptData { + selectedActor: string; + isProVersion: boolean; + tweaks: AmeTweakProperties[]; + sections: AmeSectionProperties[]; + lastUserTweakSuffix: number; + defaultCodeEditorSettings: Record ; +} + +interface AmeNamedNodeProperties { + id: string; + label: string; +} + +abstract class AmeNamedNode { + id: string; + label: string | KnockoutObservable ; + htmlId: string = ''; + + protected constructor(properties: AmeNamedNodeProperties) { + this.id = properties.id; + this.label = properties.label; + } +} + +interface AmeSettingsGroupProperties extends AmeNamedNodeProperties { + children: ConfigurationNodeProperties[]; + propertyPath?: string | null; +} + +function isAmeSettingsGroupProperties(thing: AmeNamedNodeProperties): thing is AmeSettingsGroupProperties { + const group = thing as AmeSettingsGroupProperties; + return (typeof group.children !== 'undefined'); +} + +interface AmeSettingProperties extends AmeNamedNodeProperties { + dataType: string; + inputType: string | null; + defaultValue?: any; +} + +function isAmeSettingProperties(thing: AmeNamedNodeProperties): thing is AmeSettingProperties { + return (typeof (thing as AmeSettingProperties).dataType === 'string'); +} + +abstract class AmeSetting extends AmeNamedNode { + protected static idCounter = 0; + + // noinspection JSUnusedGlobalSymbols Used in Knockout templates. + templateName: string; + inputValue: KnockoutObservable ; + readonly uniqueInputId: string; + + protected constructor(properties: AmeSettingProperties, store: AmeSettingStore, path: string[] = []) { + super(properties); + let defaultValue = null; + if (typeof properties.defaultValue !== 'undefined') { + defaultValue = properties.defaultValue; + } + this.inputValue = store.getObservableProperty(properties.id, defaultValue, path); + + AmeSetting.idCounter++; + this.uniqueInputId = 'ws-ame-gen-setting-' + AmeSetting.idCounter; + } +} + +interface AmeStringSettingProperties extends AmeSettingProperties { + syntaxHighlighting?: string; +} + +class AmeStringSetting extends AmeSetting { + syntaxHighlightingOptions: object = null; + + constructor( + properties: AmeStringSettingProperties, + module: AmeTweakManagerModule, + store: AmeSettingStore, + path: string[] = [] + ) { + super(properties, store, path); + this.templateName = 'ame-tweak-textarea-input-template'; + + if (properties.syntaxHighlighting && module) { + this.syntaxHighlightingOptions = module.getCodeMirrorOptions(properties.syntaxHighlighting); + } + } +} + +class AmeColorSetting extends AmeSetting { + constructor( + properties: AmeStringSettingProperties, + store: AmeSettingStore, + path: string[] = [] + ) { + super(properties, store, path); + this.templateName = 'ame-tweak-color-input-template'; + } +} + +class AmeBooleanSetting extends AmeSetting { + public templateName: string = 'ame-tweak-boolean-input-template'; + + constructor( + properties: AmeStringSettingProperties, + store: AmeSettingStore, + path: string[] = [] + ) { + super(properties, store, path); + + //Ensure that the value is always a boolean. + let _internalValue = this.inputValue; + if (typeof _internalValue() !== 'boolean') { + _internalValue(!!_internalValue()); + } + + this.inputValue = ko.computed ({ + read: function () { + return _internalValue(); + }, + write: function (newValue) { + if (typeof newValue !== 'boolean') { + newValue = !!newValue; + } + _internalValue(newValue); + }, + owner: this + }); + } +} + +interface AmeActorFeatureProperties extends AmeSettingsGroupProperties { + hasAccessMap: true; + defaultAccessMap?: AmeDictionary ; + enabledForActor?: AmeDictionary ; +} + +function isAmeActorFeatureProperties(thing: AmeNamedNodeProperties): thing is AmeActorFeatureProperties { + return (typeof (thing as AmeActorFeatureProperties).hasAccessMap === 'boolean'); +} + +type ConfigurationNodeProperties = AmeActorFeatureProperties | AmeSettingProperties | AmeSettingsGroupProperties; + +class AmeSettingStore { + private observableProperties: Record > = {}; + private accessMaps: Record = {}; + private readonly initialProperties: Record ; + + constructor(initialProperties: Record = {}) { + this.initialProperties = initialProperties; + } + + getObservableProperty (name: string, defaultValue: T, path: string | string[] = []): KnockoutObservable { + path = this.getFullPath(name, path); + + if (this.observableProperties.hasOwnProperty(path)) { + return this.observableProperties[path]; + } + + const _ = AmeTweakManagerModule._; + const value = _.get(this.initialProperties, path, defaultValue); + const observable = ko.observable(value); + this.observableProperties[path] = observable; + return observable; + } + + protected getFullPath(name: string, path: string | string[]): string { + if (typeof path !== 'string') { + path = path.join('.'); + } + if (path === '') { + path = name; + } else { + path = path + '.' + name; + } + return path; + } + + propertiesToJs(): Record { + const _ = AmeTweakManagerModule._; + let newProps = {}; + _.forOwn(this.observableProperties, function (observable, path) { + _.set(newProps, path, observable()); + }); + + _.forOwn(this.accessMaps, function (map, path: string) { + //Since all tweaks are disabled by default, having a tweak disabled for a role is the same + //as not having a setting, so we can save some space by removing it. This does not always + //apply to users/Super Admins because they can have precedence over roles. + let temp = map.getAll(); + let enabled: AmeDictionary = {}; + let areAllFalse = true; + for (let actorId in temp) { + if (!temp.hasOwnProperty(actorId)) { + continue; + } + + areAllFalse = areAllFalse && (!temp[actorId]); + if (!temp[actorId]) { + const actor = AmeActors.getActor(actorId); + if (actor instanceof AmeRole) { + continue; + } + } + enabled[actorId] = temp[actorId]; + } + + if (areAllFalse) { + enabled = {}; + } + + _.set(newProps, path, enabled); + }); + + return newProps; + } + + getAccessMap( + name: string, + path: string | string[] = [], + defaultAccessMap: AmeDictionary | null = null + ): AmeObservableActorSettings { + path = this.getFullPath(name, path); + const _ = AmeTweakManagerModule._; + const value = _.get(this.initialProperties, path, defaultAccessMap); + + if (!this.accessMaps.hasOwnProperty(path)) { + this.accessMaps[path] = new AmeObservableActorSettings(value); + + } + return this.accessMaps[path]; + } +} + +function isSettingStore(thing: object): thing is AmeSettingStore { + const maybe = thing as AmeSettingStore; + return (typeof maybe.getObservableProperty !== 'undefined') && (typeof maybe.propertiesToJs !== 'undefined'); +} + +class AmeCompositeNode extends AmeNamedNode { + children: KnockoutObservableArray = null; + propertyPath: string[]; + actorAccess: AmeActorAccess; + properties: AmeSettingStore; + + constructor( + properties: ConfigurationNodeProperties, + module: AmeTweakManagerModule, + store: AmeSettingStore | 'self' = null, + path: string[] = [] + ) { + super(properties); + this.id = properties.id; + this.label = properties.label; + + if (store === 'self') { + if (!this.properties) { + this.properties = new AmeSettingStore(properties); + } + store = this.properties; + } + + if (isAmeSettingsGroupProperties(properties)) { + if ((typeof properties.propertyPath === 'string') && (properties.propertyPath !== '')) { + this.propertyPath = properties.propertyPath.split('.'); + } else { + this.propertyPath = []; + } + if (path.length > 0) { + this.propertyPath = path.concat(this.propertyPath); + } + + let children = []; + if (properties.children && (properties.children.length > 0)) { + for (let i = 0; i < properties.children.length; i++) { + const props = properties.children[i]; + let child; + if (isAmeSettingProperties(props)) { + child = AmeCompositeNode.createSetting(props, module, store, this.propertyPath); + } else { + child = new AmeCompositeNode(props, module, store, this.propertyPath); + } + if (child) { + children.push(child); + } + } + } + + this.children = ko.observableArray(children); + } + + if (isAmeActorFeatureProperties(properties)) { + let name = (store === this.properties) ? 'enabledForActor' : this.id; + const defaultAccess = (typeof properties.defaultAccessMap !== 'undefined') ? properties.defaultAccessMap : null; + this.actorAccess = new AmeActorAccess( + store.getAccessMap(name, path, defaultAccess), + module, + this.children + ); + } + } + + static createSetting( + properties: AmeSettingProperties, + module: AmeTweakManagerModule, + store: AmeSettingStore, + path: string[] = [] + ): AmeSetting { + const inputType = properties.inputType ? properties.inputType : properties.dataType; + + switch (inputType) { + case 'text': + case 'textarea': + case 'string': + return new AmeStringSetting(properties, module, store, path); + case 'color': + return new AmeColorSetting(properties, store, path); + case 'boolean': + return new AmeBooleanSetting(properties, store, path); + default: + if (console && console.error) { + console.error('Unknown setting input type "%s"', inputType); + } + return null; + } + } +} + +class AmeActorAccess { + isChecked: KnockoutComputed ; + protected enabledForActor: AmeObservableActorSettings; + protected module: AmeTweakManagerModule; + isIndeterminate: KnockoutComputed ; + + constructor( + actorSettings: AmeObservableActorSettings, + module: AmeTweakManagerModule, + children: AmeCompositeNode['children'] = null + ) { + this.module = module; + this.enabledForActor = actorSettings; + + let _isIndeterminate = ko.observable (false); + this.isIndeterminate = ko.computed (() => { + if (module.selectedActor() !== null) { + return false; + } + return _isIndeterminate(); + }); + + this.isChecked = ko.computed ({ + read: () => { + const selectedActor = this.module.selectedActor(); + + if (selectedActor === null) { + //All: Checked only if it's checked for all actors. + const allActors = this.module.actorSelector.getVisibleActors(); + let isEnabledForAll = true, isEnabledForAny = false; + for (let index = 0; index < allActors.length; index++) { + if (this.enabledForActor.get(allActors[index].getId(), false)) { + isEnabledForAny = true; + } else { + isEnabledForAll = false; + } + } + + _isIndeterminate(isEnabledForAny && !isEnabledForAll); + + return isEnabledForAll; + } + + //Is there an explicit setting for this actor? + let ownSetting = this.enabledForActor.get(selectedActor.getId(), null); + if (ownSetting !== null) { + return ownSetting; + } + + if (selectedActor instanceof AmeUser) { + //The "Super Admin" setting takes precedence over regular roles. + if (selectedActor.isSuperAdmin) { + let superAdminSetting = this.enabledForActor.get(AmeSuperAdmin.permanentActorId, null); + if (superAdminSetting !== null) { + return superAdminSetting; + } + } + + //Is it enabled for any of the user's roles? + for (let i = 0; i < selectedActor.roles.length; i++) { + let groupSetting = this.enabledForActor.get('role:' + selectedActor.roles[i], null); + if (groupSetting === true) { + return true; + } + } + } + + //All tweaks are unchecked by default. + return false; + }, + write: (checked: boolean) => { + const selectedActor = this.module.selectedActor(); + if (selectedActor === null) { + //Enable/disable this tweak for all actors. + if (checked === false) { + //Since false is the default, this is the same as removing/resetting all values. + this.enabledForActor.resetAll(); + } else { + const allActors = this.module.actorSelector.getVisibleActors(); + for (let i = 0; i < allActors.length; i++) { + this.enabledForActor.set(allActors[i].getId(), checked); + } + } + } else { + this.enabledForActor.set(selectedActor.getId(), checked); + } + + //Apply the same setting to all children. + if (children) { + const childrenArray = children(); + for (let i = 0; i < childrenArray.length; i++) { + const child = childrenArray[i]; + if ((child instanceof AmeCompositeNode) && child.actorAccess) { + child.actorAccess.isChecked(checked); + } + } + } + } + }); + } +} + +interface AmeSavedTweakProperties { + id: string; + enabledForActor?: AmeDictionary ; +} + +interface AmeTweakProperties extends AmeSavedTweakProperties, AmeActorFeatureProperties { + description?: string; + parentId?: string; + sectionId?: string; + + isUserDefined?: boolean; + typeId?: string; + + //User-defined tweaks can have additional arbitrary properties. + [key: string]: any; +} + +class AmeTweakItem extends AmeCompositeNode { + label: KnockoutObservable ; + + public readonly isUserDefined: boolean; + private readonly initialProperties: AmeSavedTweakProperties = null; + + private section: AmeTweakSection = null; + private parent: AmeTweakItem = null; + + constructor(properties: AmeTweakProperties, module: AmeTweakManagerModule) { + super(properties, module, 'self'); + + this.isUserDefined = properties.isUserDefined ? properties.isUserDefined : false; + if (this.isUserDefined) { + this.initialProperties = properties; + } + + if (this.isUserDefined) { + this.label = ko.observable(properties.label); + } else { + this.label = ko.pureComputed(function () { + return properties.label; + }); + } + + this.htmlId = 'ame-tweak-' + AmeTweakManagerModule.slugify(this.id); + } + + toJs(): AmeSavedTweakProperties { + let result: AmeSavedTweakProperties = { + id: this.id + }; + + const _ = AmeTweakManagerModule._; + if (this.properties) { + result = _.defaults(result, this.properties.propertiesToJs()); + } + + if (!this.isUserDefined) { + return result; + } else { + let props: AmeTweakProperties = result as AmeTweakProperties; + props.isUserDefined = this.isUserDefined; + props.label = this.label(); + props.sectionId = this.section ? this.section.id : null; + props.parentId = this.parent ? this.parent.id : null; + + props = _.defaults( + props, + _.omit(this.initialProperties, 'userInputValue', 'enabledForActor') + ); + return props; + } + } + + setSection(section: AmeTweakSection) { + this.section = section; + return this; + } + + setParent(tweak: AmeTweakItem) { + this.parent = tweak; + return this; + } + + getSection(): AmeTweakSection { + return this.section; + } + + getParent(): AmeTweakItem { + return this.parent; + } + + addChild(tweak: AmeTweakItem) { + this.children.push(tweak); + tweak.setParent(this); + return this; + } + + removeChild(tweak: AmeTweakItem) { + this.children.remove(tweak); + } + + getEditableProperty(key: string): KnockoutObservable { + if (this.properties) { + return this.properties.getObservableProperty(key, ''); + } + } + + getTypeId(): string | null { + if (!this.isUserDefined || !this.initialProperties) { + return null; + } + if ((this.initialProperties as AmeTweakProperties).typeId) { + return (this.initialProperties as AmeTweakProperties).typeId; + } + return null; + } +} + +interface AmeSectionProperties { + id: string; + label: string; + priority: number | null; +} + +class AmeTweakSection { + id: string; + label: string; + tweaks: KnockoutObservableArray ; + isOpen: KnockoutObservable ; + + footerTemplateName: string = null; + + constructor(properties: AmeSectionProperties) { + this.id = properties.id; + this.label = properties.label; + this.isOpen = ko.observable (true); + this.tweaks = ko.observableArray([]); + } + + addTweak(tweak: AmeTweakItem) { + this.tweaks.push(tweak); + tweak.setSection(this); + } + + removeTweak(tweak: AmeTweakItem) { + this.tweaks.remove(tweak); + } + + hasContent() { + return this.tweaks().length > 0; + } + + toggle() { + this.isOpen(!this.isOpen()); + } +} + +class AmeTweakManagerModule { + static _ = wsAmeLodash; + static readonly openSectionCookieName = 'ame_tmce_open_sections'; + + readonly actorSelector: AmeActorSelector; + selectedActorId: KnockoutComputed ; + selectedActor: KnockoutComputed ; + + private tweaksById: { [id: string]: AmeTweakItem } = {}; + private sectionsById: AmeDictionary = {}; + sections: AmeTweakSection[] = []; + + settingsData: KnockoutObservable ; + isSaving: KnockoutObservable ; + + private readonly openSectionIds: KnockoutComputed ; + + readonly adminCssEditorDialog: AmeEditAdminCssDialog; + private lastUserTweakSuffix: number = 0; + + public readonly cssHighlightingOptions: Record ; + + constructor(scriptData: AmeTweakManagerScriptData) { + const _ = AmeTweakManagerModule._; + + this.actorSelector = new AmeActorSelector(AmeActors, scriptData.isProVersion); + this.selectedActorId = this.actorSelector.createKnockoutObservable(ko); + this.selectedActor = ko.computed (() => { + const id = this.selectedActorId(); + if (id === null) { + return null; + } + return AmeActors.getActor(id); + }); + + //Reselect the previously selected actor. + this.selectedActorId(scriptData.selectedActor); + + //Set syntax highlighting options. + this.cssHighlightingOptions = _.merge( + {}, + scriptData.defaultCodeEditorSettings, + { + 'codemirror': { + 'mode': 'css', + 'lint': true, + 'autoCloseBrackets': true, + 'matchBrackets': true + } + } + ); + + //Sort sections by priority, then by label. + let sectionData = _.sortByAll(scriptData.sections, ['priority', 'label']); + //Register sections. + _.forEach(sectionData, (properties) => { + let section = new AmeTweakSection(properties); + this.sectionsById[section.id] = section; + this.sections.push(section); + }); + const firstSection = this.sections[0]; + + _.forEach(scriptData.tweaks, (properties) => { + const tweak = new AmeTweakItem(properties, this); + this.tweaksById[tweak.id] = tweak; + + if (properties.parentId && this.tweaksById.hasOwnProperty(properties.parentId)) { + this.tweaksById[properties.parentId].addChild(tweak); + } else { + let ownerSection = firstSection; + if (properties.sectionId && this.sectionsById.hasOwnProperty(properties.sectionId)) { + ownerSection = this.sectionsById[properties.sectionId]; + } + ownerSection.addTweak(tweak); + } + }); + + //Remove empty sections. + this.sections = _.filter(this.sections, function (section) { + return section.hasContent(); + }); + + //Add the tweak creation button to the Admin CSS section. + if (this.sectionsById.hasOwnProperty('admin-css')) { + this.sectionsById['admin-css'].footerTemplateName = 'ame-admin-css-section-footer'; + } + + //By default, all sections except the first one are closed. + //The user can open/close sections and we automatically remember their state. + this.openSectionIds = ko.computed ({ + read: () => { + let result = []; + _.forEach(this.sections, section => { + if (section.isOpen()) { + result.push(section.id); + } + }); + return result; + }, + write: (sectionIds: string[]) => { + const openSections = _.indexBy(sectionIds); + _.forEach(this.sections, section => { + section.isOpen(openSections.hasOwnProperty(section.id)); + }); + } + }); + this.openSectionIds.extend({rateLimit: {timeout: 1000, method: 'notifyWhenChangesStop'}}); + + let initialState: string[] = null; + let cookieValue = jQuery.cookie(AmeTweakManagerModule.openSectionCookieName); + if ((typeof cookieValue === 'string') && JSON && JSON.parse) { + let storedState = JSON.parse(cookieValue); + if (_.isArray (storedState)) { + initialState = _.intersection(_.keys(this.sectionsById), storedState); + } + } + + if (initialState !== null) { + this.openSectionIds(initialState); + } else { + this.openSectionIds([_.first(this.sections).id]); + } + + this.openSectionIds.subscribe((sectionIds) => { + jQuery.cookie(AmeTweakManagerModule.openSectionCookieName, ko.toJSON(sectionIds), {expires: 90}); + }); + + if (scriptData.lastUserTweakSuffix) { + this.lastUserTweakSuffix = scriptData.lastUserTweakSuffix; + } + + this.adminCssEditorDialog = new AmeEditAdminCssDialog(this); + + this.settingsData = ko.observable (''); + this.isSaving = ko.observable (false); + } + + saveChanges() { + this.isSaving(true); + const _ = wsAmeLodash; + + let data = { + 'tweaks': _.indexBy(_.invoke(this.tweaksById, 'toJs'), 'id'), + 'lastUserTweakSuffix': this.lastUserTweakSuffix + }; + this.settingsData(ko.toJSON(data)); + return true; + } + + addAdminCssTweak(label: string, css: string) { + this.lastUserTweakSuffix++; + + let slug = AmeTweakManagerModule.slugify(label); + if (slug !== '') { + slug = '-' + slug; + } + + let props: AmeTweakProperties = { + label: label, + id: 'utw-' + this.lastUserTweakSuffix + slug, + isUserDefined: true, + sectionId: 'admin-css', + typeId: 'admin-css', + children: [], + hasAccessMap: true + }; + props['css'] = css; + + const cssInput: AmeStringSettingProperties = { + id: 'css', + label: '', + dataType: 'string', + inputType: 'textarea', + syntaxHighlighting: 'css' + }; + props.children.push(cssInput); + + const newTweak = new AmeTweakItem(props, this); + this.tweaksById[newTweak.id] = newTweak; + this.sectionsById['admin-css'].addTweak(newTweak) + } + + static slugify(input: string): string { + const _ = AmeTweakManagerModule._; + let output = _.deburr(input); + output = output.replace(/[^a-zA-Z0-9 \-]/, ''); + return _.kebabCase(output); + } + + launchTweakEditor(tweak: AmeTweakItem) { + // noinspection JSRedundantSwitchStatement + switch (tweak.getTypeId()) { + case 'admin-css': + this.adminCssEditorDialog.selectedTweak = tweak; + this.adminCssEditorDialog.open(); + break; + default: + alert('Error: Editor not implemented! This is probably a bug.'); + } + } + + confirmDeleteTweak(tweak: AmeTweakItem) { + if (!tweak.isUserDefined || !confirm('Delete this tweak?')) { + return; + } + this.deleteTweak(tweak); + } + + protected deleteTweak(tweak: AmeTweakItem) { + const section = tweak.getSection(); + if (section) { + section.removeTweak(tweak); + } + const parent = tweak.getParent(); + if (parent) { + parent.removeChild(tweak); + } + delete this.tweaksById[tweak.id]; + } + + getCodeMirrorOptions(mode: string) { + if (mode === 'css') { + return this.cssHighlightingOptions; + } + return null; + } +} + +class AmeEditAdminCssDialog implements AmeKnockoutDialog { + jQueryWidget: JQuery; + isOpen: KnockoutObservable ; + autoCancelButton: boolean = false; + + options: AmeDictionary = { + minWidth: 400 + }; + + isAddButtonEnabled: KnockoutComputed ; + tweakLabel: KnockoutObservable ; + cssCode: KnockoutObservable ; + confirmButtonText: KnockoutObservable ; + title: KnockoutObservable ; + + selectedTweak: AmeTweakItem = null; + + private manager: AmeTweakManagerModule; + + constructor(manager: AmeTweakManagerModule) { + const _ = AmeTweakManagerModule._; + this.manager = manager; + + this.tweakLabel = ko.observable(''); + this.cssCode = ko.observable(''); + this.confirmButtonText = ko.observable('Add Snippet'); + this.title = ko.observable(null); + + this.isAddButtonEnabled = ko.computed(() => { + return !((_.trim(this.tweakLabel()) === '') || (_.trim(this.cssCode()) === '')); + }); + this.isOpen = ko.observable(false); + } + + onOpen(event, ui) { + if (this.selectedTweak) { + this.tweakLabel(this.selectedTweak.label()); + this.title('Edit admin CSS snippet'); + this.confirmButtonText('Save Changes'); + + const cssProperty = this.selectedTweak.getEditableProperty('css'); + this.cssCode(cssProperty ? cssProperty() : ''); + } else { + this.tweakLabel(''); + this.cssCode(''); + this.title('Add admin CSS snippet'); + this.confirmButtonText('Add Snippet'); + } + } + + onConfirm() { + if (this.selectedTweak) { + //Update the existing tweak. + this.selectedTweak.label(this.tweakLabel()); + this.selectedTweak.getEditableProperty('css')(this.cssCode()); + } else { + //Create a new tweak. + this.manager.addAdminCssTweak( + this.tweakLabel(), + this.cssCode() + ); + } + this.close(); + } + + onClose() { + this.selectedTweak = null; + } + + close() { + this.isOpen(false); + } + + open() { + this.isOpen(true); + } +} + +ko.bindingHandlers.ameCodeMirror = { + init: function (element, valueAccessor, allBindings) { + if (!wp.hasOwnProperty('codeEditor') || !wp.codeEditor.initialize) { + return; + } + let parameters = ko.unwrap(valueAccessor()); + if (!parameters) { + return; + } + + let options; + let refreshTrigger: KnockoutObservable ; + if (parameters.options) { + options = parameters.options; + if (parameters.refreshTrigger) { + refreshTrigger = parameters.refreshTrigger; + } + } else { + options = parameters; + } + + let result = wp.codeEditor.initialize(element, options); + const cm = result.codemirror; + + //Synchronise the editor contents with the observable passed to the "value" binding. + let valueObservable: KnockoutObservable = allBindings.get('value'); + if (!ko.isObservable(valueObservable)) { + valueObservable = null; + } + + let subscription = null; + let changeHandler = null; + if (valueObservable !== null) { + //Update the observable when the contents of the editor change. + let ignoreNextUpdate = false; + changeHandler = function () { + //This will trigger our observable subscription (see below). + //We need to ignore that trigger to avoid recursive or duplicated updates. + ignoreNextUpdate = true; + valueObservable(cm.doc.getValue()); + }; + cm.on('changes', changeHandler); + + //Update the editor when the observable changes. + subscription = valueObservable.subscribe(function (newValue) { + if (ignoreNextUpdate) { + ignoreNextUpdate = false; + return; + } + cm.doc.setValue(newValue); + ignoreNextUpdate = false; + }); + } + + //Refresh the size of the editor element when an observable changes value. + let refreshSubscription: KnockoutSubscription = null; + if (refreshTrigger) { + refreshSubscription = refreshTrigger.subscribe(function () { + cm.refresh(); + }); + } + + //If the editor starts out hidden - for example, because it's inside a collapsed section - it will + //render incorrectly. To fix that, let's refresh it the first time it becomes visible. + if (!jQuery(element).is(':visible') && (typeof IntersectionObserver !== 'undefined')) { + const observer = new IntersectionObserver( + function (entries) { + for (let i = 0; i < entries.length; i++) { + if (entries[i].isIntersecting) { + //The editor is at least partially visible now. + observer.disconnect(); + cm.refresh(); + break; + } + } + }, + { + //Use the browser viewport. + root: null, + //The threshold is somewhat arbitrary. Any value will work, but a lower setting means + //that the user is less likely to see an incorrectly rendered editor. + threshold: 0.05 + } + ); + observer.observe(cm.getWrapperElement()); + } + + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + //Remove subscriptions and event handlers. + if (subscription) { + subscription.dispose(); + } + if (refreshSubscription) { + refreshSubscription.dispose(); + } + if (changeHandler) { + cm.off('changes', changeHandler); + } + + //Destroy the CodeMirror instance. + jQuery(cm.getWrapperElement()).remove(); + }); + } +}; + +jQuery(function () { + ameTweakManager = new AmeTweakManagerModule(wsTweakManagerData); + ko.applyBindings(ameTweakManager, document.getElementById('ame-tweak-manager')); +}); \ No newline at end of file diff --git a/extras/modules/tweaks/tweaks-template.php b/extras/modules/tweaks/tweaks-template.php new file mode 100644 index 0000000..62004eb --- /dev/null +++ b/extras/modules/tweaks/tweaks-template.php @@ -0,0 +1,230 @@ + + + + ++