├── .browserslistrc ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── SUPPORT.md ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── assets ├── fonts │ ├── OSDXPI.eot │ ├── OSDXPI.svg │ ├── OSDXPI.ttf │ └── OSDXPI.woff ├── images │ ├── OSDXP-logo-100.png │ ├── OSDXP-logo-inverse-100.png │ ├── backburger-inverse.png │ ├── backburger.png │ ├── osDXP_logo.svg │ ├── placeholder.png │ └── sample.png ├── js │ ├── main.js │ └── upload-toggle.js └── sass │ ├── _admin-menu.scss │ ├── _admin.scss │ ├── _available-modules.scss │ ├── _common.scss │ ├── _forms.scss │ ├── _icons.scss │ ├── _list-tables.scss │ ├── _mixins.scss │ ├── _transition.scss │ ├── _variables.scss │ ├── _welcome-panel.scss │ ├── admin-bar.scss │ ├── style-admin.scss │ ├── style-wp-admin.scss │ └── wp-admin-bar.scss ├── build ├── admin-bar.css ├── admin-bar.min.css ├── app.js ├── app.min.js ├── notifications.js ├── style-admin.css ├── style-admin.min.css ├── style-wp-admin.css ├── style-wp-admin.min.css ├── wp-admin-bar.css └── wp-admin-bar.min.css ├── composer.json ├── composer.lock ├── default-available-modules.json ├── gulpfile.js ├── includes ├── assets.php ├── class-licenseapi.php ├── class-osdxp-render-available-modules.php ├── config.php ├── core.php ├── dashboard.php ├── dependencies │ ├── plugin-update-checker │ │ ├── class-osdxp-module-update-checker-ui.php │ │ └── class-osdxp-module-update-checker.php │ └── wordpress │ │ ├── class-osdxp-bulk-module-upgrader-skin.php │ │ ├── class-osdxp-module-installer-skin.php │ │ ├── class-osdxp-module-upgrader-skin.php │ │ ├── class-osdxp-module-upgrader.php │ │ ├── class-osdxp-modules-list-table.php │ │ ├── module-install.php │ │ ├── modules-print-updates.php │ │ └── modules-update-information.php ├── licensing.php ├── menus.php ├── modules.php ├── notifications.php └── utils.php ├── languages └── en_US.pot ├── module_update_info.json.example ├── osdxp-dashboard.php ├── package-lock.json ├── package.json ├── phpcs.xml └── templates ├── dashboard-actions-editor.php ├── dashboard-actions-network-admin.php ├── dashboard-actions-single-admin.php ├── modules-information.php ├── modules-list-available.php ├── modules-list-installed.php └── modules-update.php /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | ie >= 8 3 | edge >= 15 4 | ie_mob >= 10 5 | ff >= 45 6 | chrome >= 45 7 | safari >= 7 8 | opera >= 23 9 | ios >= 7 10 | android >= 4 11 | bb >= 10 -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to osDXP Dashboard 2 | 3 | Looking to contribute something to osDXP Dashboard? **Here's how you can help.** 4 | 5 | Please take a moment to review this document in order to make the contribution 6 | process easy and effective for everyone involved. 7 | 8 | Following these guidelines helps to communicate that you respect the time of 9 | the developers managing and developing this open source project. In return, 10 | they should reciprocate that respect in addressing your issue or assessing 11 | patches and features. 12 | 13 | 14 | ## Using the issue tracker 15 | 16 | The [issue tracker](https://github.com/osDXP/osdxp-dashboard/issues) is 17 | the preferred channel for [bug reports](#bug-reports), [features requests](#feature-requests) 18 | and [submitting pull requests](#pull-requests), but please respect the following 19 | restrictions: 20 | 21 | * Please **do not** use the issue tracker for personal support requests. The osDXP [Slack](https://osDXP.slack.com/) or [Contact Page](https://osdxp.org/contact/) are better places to get help. 22 | 23 | * Please **do not** derail or troll issues. Keep the discussion on topic and 24 | respect the opinions of others. 25 | 26 | * Please **do not** post comments consisting solely of "+1" or ":thumbsup:". 27 | Use [GitHub's "reactions" feature](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) 28 | instead. We reserve the right to delete comments which violate this rule. 29 | 30 | * Please **do not** open issues regarding [osDXP modules](https://osdxp.org/modules/). 31 | Instead, please email any questions or feedback regarding those modules to their respective development team or reach out on Slack. 32 | 33 | 34 | ## Issues and labels 35 | 36 | Our bug tracker utilizes several labels to help organize and identify issues. Here's what they represent and how we use them: 37 | 38 | - `good first issue` - GitHub will help potential first-time contributors discover issues that have this label. 39 | - `bug` - Issues that have been confirmed with a reduced test case and identify a bug in osDXP Dashboard. 40 | - `css` - Issues stemming from our compiled CSS or source Sass files. 41 | - `docs` - Issues for improving or updating our documentation. 42 | - `feature` - Issues asking for a new feature to be added, or an existing one to be extended or modified. 43 | - `help wanted` - Issues we need or would love help from the community to resolve. 44 | - `js` - Issues stemming from our compiled or source JavaScript files. 45 | - `meta` - Issues with the project itself or our GitHub repository. 46 | - `duplicate` - This issue or pull request already exists. 47 | 48 | For a complete look at our labels, see the [project labels page](https://github.com/osDXP/osdxp-dashboard/labels). 49 | 50 | 51 | ## Bug reports 52 | 53 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 54 | Good bug reports are extremely helpful, so thanks! 55 | 56 | Guidelines for bug reports: 57 | 58 | 0. **Validate your code** to ensure your 59 | problem isn't caused by a simple error in your own code. 60 | 61 | 1. **Use the GitHub issue search** — check if the issue has already been 62 | reported. 63 | 64 | 2. **Check if the issue has been fixed** — try to reproduce it using the 65 | latest `master` or `develop` branch in the repository. 66 | 67 | 3. **Isolate the problem** — ideally create or record a live example. 68 | 69 | 70 | A good bug report shouldn't leave others needing to chase you up for more 71 | information. Please try to be as detailed as possible in your report. What is 72 | your environment(installed plugins/modules, WordPress version, osDXP Dashboard version)? What steps will reproduce the issue? What browser(s) and serverstack 73 | experience the problem? What 74 | would you expect to be the outcome? All these details will help people to fix 75 | any potential bugs. 76 | 77 | Example: 78 | 79 | > Short and descriptive example bug report title 80 | > 81 | > A summary of the issue and the browser/serverstack/WordPress environment in which it occurs. If 82 | > suitable, include the steps required to reproduce the bug. 83 | > 84 | > 1. This is the first step 85 | > 2. This is the second step 86 | > 3. Further steps, etc. 87 | > 88 | > 89 | > Any other information you want to share that is relevant to the issue being 90 | > reported. This might include the lines of code that you have identified as 91 | > causing the bug, and potential solutions (and your opinions on their 92 | > merits). 93 | 94 | ## Feature requests 95 | 96 | Feature requests are welcome. But take a moment to find out whether your idea 97 | fits with the scope and aims of the project. It's up to *you* to make a strong 98 | case to convince the project's developers of the merits of this feature. Please 99 | provide as much detail and context as possible. 100 | 101 | 102 | ## Pull requests 103 | 104 | Good pull requests—patches, improvements, new features—are a fantastic 105 | help. They should remain focused in scope and avoid containing unrelated 106 | commits. 107 | 108 | **Please ask first** before embarking on any significant pull request (e.g. 109 | implementing features, refactoring code), otherwise you risk spending 110 | a lot of time working on something that the project's developers 111 | might not want to merge into the project. 112 | 113 | Please adhere to the [coding guidelines](#code-guidelines) used throughout the 114 | project (indentation, accurate comments, etc.) and any other requirements 115 | (such as test coverage). 116 | 117 | **Do not edit compiled assets or vendor dependencies directly!** 118 | Those files are either automatically generated in the case of js or css assets 119 | or ignored from the repo codebase in the case of dependencies(`vendor` folder). 120 | You should edit the source files for assets or extend vendor dependencies. 121 | 122 | Adhering to the following process is the best way to get your work 123 | included in the project: 124 | 125 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork, 126 | and configure the remotes: 127 | 128 | ```bash 129 | # Clone your fork of the repo into the current directory 130 | git clone https://github.com//osdxp-dashboard.git 131 | # Navigate to the newly cloned directory 132 | cd osdxp-dashboard 133 | # Assign the original repo to a remote called "upstream" 134 | git remote add upstream https://github.com/osDXP/osdxp-dashboard.git 135 | ``` 136 | 137 | 2. If you cloned a while ago, get the latest changes from upstream: 138 | 139 | ```bash 140 | git checkout develop 141 | git pull upstream develop 142 | ``` 143 | 144 | 3. Create a new topic branch (off the main project `develop` branch) to 145 | contain your feature, change, or fix: 146 | 147 | ```bash 148 | git checkout -b 149 | ``` 150 | 151 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 152 | message guidelines](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 153 | or your code is unlikely be merged into the main project. Use Git's 154 | [interactive rebase](https://help.github.com/articles/about-git-rebase/) 155 | feature to tidy up your commits before making them public. 156 | 157 | 5. Locally merge (or rebase) the upstream `develop` branch into your topic branch: 158 | 159 | ```bash 160 | git pull [--rebase] upstream develop 161 | ``` 162 | 163 | 6. Push your topic branch up to your fork: 164 | 165 | ```bash 166 | git push origin 167 | ``` 168 | 169 | 7. [Open a Pull Request](https://help.github.com/articles/about-pull-requests/) 170 | with a clear title and description against the `develop` branch. 171 | 172 | **IMPORTANT**: By submitting a patch, you agree to allow the project owners to 173 | license your work under the terms of the [GPLv2 License](../LICENSE). 174 | 175 | 176 | ## Code guidelines 177 | 178 | ### PHP 179 | osDXP repos follow an extended [PSR12 ruleset](../phpcs.xml) that includes WordPress Security sniffs with the added change of preffering tabs instead of spaces for indentation. 180 | **You code should adhere to this ruleset.** 181 | 182 | ### CSS 183 | - Classes should be named following the [BEM conventions](http://getbem.com/naming/). 184 | - Your compiled CSS should nest no more than 4 levels deep, preferably no more than 3 levels. 185 | - Follow [ITCSS](https://www.creativebloq.com/web-design/manage-large-css-projects-itcss-101517528) principles for organizing styles. 186 | 187 | ### JS 188 | We adhere to the [AirBnB JavaScript Style Guide](https://github.com/airbnb/javascript) for project JavaScript code. [All rules apply](https://github.com/airbnb/javascript/blob/master/README.md#table-of-contents) with the following exceptions: 189 | - Ignore rules related to IE8 support. We don't generally do it, and sometimes it can be useful to use a normal property overridden into a different one (such as a CSS styles object to pass to jQuery). 190 | - Put all functions at the end of scope, possibly after a return. Doing this comes from the Angular guide, and makes it easier to follow a narrative flow of the code. We see the behaviors, then can quickly dig into the implementation if we wish to. (Credit to [John Papa's Angular Style guide](https://github.com/johnpapa/angular-styleguide) for this idea) 191 | 192 | ### Checking coding style 193 | Run `phpcs --standard=phpcs.xml` before committing to ensure your changes follow our coding standards. 194 | 195 | ## License 196 | By contributing your code, you agree to license your contribution under the [GPLv2 License](../LICENSE). -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Tell us about a bug you may have identified in osDXP Dashboard. 4 | 5 | --- 6 | Before opening: 7 | 8 | - [Search for duplicate or closed issues](https://github.com/osDXP/osdxp-dashboard/issues?utf8=%E2%9C%93&q=is%3Aissue) 9 | - Validate and [lint](https://github.com/osDXP/osdxp-dashboard/blob/master/phpcs.xml) any PHP code to avoid common problems 10 | - Read the [contributing guidelines](https://github.com/osDXP/osdxp-dashboard/blob/master/.github/CONTRIBUTING.md) 11 | 12 | Bug reports must include: 13 | 14 | - Operating system and version (Windows, macOS, Android, iOS, Win10 Mobile) 15 | - Browser and version (Chrome, Firefox, Safari, IE, MS Edge, Opera 15+, Android Browser) 16 | - Serverstack(with versions) and WordPress environment(osDXP Dashboard version, WordPress version, installed plugins, multi or single site) 17 | - Ideally create or record a live example of the bug -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for a new feature in osDXP Dashboard. 4 | 5 | --- 6 | 7 | Before opening: 8 | 9 | - [Search for duplicate or closed issues](https://github.com/osDXP/osdxp-dashboard/issues?utf8=%E2%9C%93&q=is%3Aissue) 10 | - Read the [contributing guidelines](https://github.com/osDXP/osdxp-dashboard/blob/master/.github/CONTRIBUTING.md) 11 | 12 | Feature requests must include: 13 | 14 | - As much detail as possible for what we should add and why it's important to osDXP Dashboard 15 | - Relevant resources wherever possible -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | ## Testing instructions 6 | 7 | 8 | 9 | ## Screenshots 10 | 11 | ## Checklist: 12 | - [ ] I've tested the code. 13 | - [ ] My code follows the osDXP custom PSR12 code style. 14 | - [ ] My code follows the accessibility standards. 15 | - [ ] My code follows the PHP DocBlock documentation standards. -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | ### Bug reports 2 | 3 | See the [contributing guidelines](CONTRIBUTING.md) for sharing bug reports. 4 | 5 | ### How-to 6 | 7 | For general troubleshooting or help getting started: 8 | 9 | - Join [the official Slack room](https://osdxp.slack.com/). 10 | - Ask and explore the FAQ page on the [osDXP website](https://osdxp.org). -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.todo 3 | build/*.map 4 | vendor/ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | A historical record of notable changes to `osdxp-dashboard` will be documented in this file. 4 | 5 | ## [v1.1.1 (2019-12-27)](https://github.com/osDXP/osdxp-dashboard/releases/tag/v1.1.1) 6 | - Apply proper colspan to module update messages 7 | - Refactor dashboard templates logic 8 | - Refactor redirect path for user logins that are using osDXP 9 | - Account for osDXP dashboard status when deciding redirect path when checking for updates 10 | - Update default available modules json file 11 | - Reimplement available modules in regular admin on a multisite 12 | - Fix checking for new modules on a single site in a multisite instance 13 | - Fix proper path when switching to regular WordPress interface 14 | 15 | ## [v1.1.0 (2019-12-05)](https://github.com/osDXP/osdxp-dashboard/releases/tag/v1.1.0) 16 | - Update repository meta files 17 | - Make plugin agnostic of integrations 18 | - Register assets by default and only enqueue them conditionally 19 | - Use SCRIPT_DEBUG instead of WP_DEBUG when deciding what type of assets to load (production or development) 20 | 21 | ## [v1.0.3 (2019-11-05)](https://github.com/osDXP/osdxp-dashboard/releases/tag/v1.0.3) 22 | - Update default available modules `json` 23 | - Add slug filtering for modules 24 | - Update `yahnis-elsts/plugin-update-checker` dep to `~4.8.0` 25 | - Shift parent updater initialization logic to a point before member access 26 | - Files with effects now loaded by main plugin 27 | - Autoload file now loaded only if exists 28 | 29 | ## [v1.0.2 (2019-11-01)](https://github.com/osDXP/osdxp-dashboard/releases/tag/v1.0.2) 30 | - Change osDXP logo from `png` to `svg` 31 | - Change Dashboard action icons to be inline with the sidebar 32 | - Fix incorrect instances of osDXP title 33 | - Fix dashboard title position 34 | 35 | ## [v1.0.1 (2019-10-01)](https://github.com/osDXP/osdxp-dashboard/releases/tag/v1.0.1) 36 | - Add classmap autoloading via composer 37 | - Cleanup available modules endpoint 38 | 39 | ## [v1.0 (2019-10-01)](https://github.com/osDXP/osdxp-dashboard/releases/tag/v1.0) 40 | - Initial release -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at CodeOfConduct@osdxp.org. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | osDXP logo 4 | 5 |

6 | 7 |

osDXP Dashboard

8 | 9 | 10 |

11 | An Augmentation of WordPress, creating a task-oriented Digital Experience Platform environment. 12 |
13 | Explore osDXP Dashboard docs » 14 |
15 |
16 | Report bug 17 | · 18 | Request feature 19 | · 20 | Modules 21 | · 22 | Partners 23 |

24 | 25 | 26 | ## Table of contents 27 | 28 | - [Quick start](#quick-start) 29 | - [Requirements](#requirements) 30 | - [Status](#status) 31 | - [Bugs and feature requests](#bugs-and-feature-requests) 32 | - [Documentation](#documentation) 33 | - [Contributing](#contributing) 34 | - [Community](#community) 35 | - [Versioning](#versioning) 36 | - [Changelog](#changelog) 37 | - [License](#license) 38 | 39 | 40 | ## Quick start 41 | 42 | Several quick start options are available: 43 | 44 | - [Download the latest release.](https://github.com/osDXP/osdxp-dashboard/releases/) 45 | - Clone the repo: `git clone https://github.com/osDXP/osdxp-dashboard.git` (run `composer install` after cloning) 46 | - Install with [Composer](https://getcomposer.org/): `composer require osDXP/osdxp-dashboard` 47 | 48 | Read the [Getting started page](https://osdxp.github.io/docs/getting-started/) for more information. 49 | 50 | 51 | ## Requirements 52 | 53 | osDXP Dashboard is dependent on: 54 | 55 | - Minimum **PHP** version **7.2** 56 | - Minimum **WordPress** version **5.2** 57 | 58 | 59 | ## Status 60 | 61 | [![Latest Release](https://img.shields.io/github/v/release/osDXP/osdxp-dashboard)](https://github.com/osDXP/osdxp-dashboard/releases) 62 | [![CSS gzip size](https://img.badgesize.io/osDXP/osdxp-dashboard/master/build/style-admin.min.css?compression=gzip&label=CSS+gzip+size)](https://github.com/osDXP/osdxp-dashboard/tree/master/build/style-admin.min.css) 63 | [![JS gzip size](https://img.badgesize.io/osDXP/osdxp-dashboard/master/build/app.min.js?compression=gzip&label=JS+gzip+size)](https://github.com/osDXP/osdxp-dashboard/tree/master/build/app.min.js) 64 | 65 | ## Bugs and feature requests 66 | 67 | Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/osDXP/osdxp-dashboard/blob/master/.github/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/osDXP/osdxp-dashboard/issues/new). 68 | 69 | 70 | ## Documentation 71 | 72 | osDXP Dashboard's documentation, included in the [`docs`](https://github.com/osDXP/docs/) repo of the osDXP organization, is built with [MkDocs](https://www.mkdocs.org/) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) and publicly hosted on GitHub Pages at . The docs may also be run locally. 73 | 74 | 75 | ## Contributing 76 | 77 | Please read through our [contributing guidelines](https://github.com/osDXP/osdxp-dashboard/blob/master/.github/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. 78 | 79 | 80 | ## Community 81 | 82 | Get updates on osDXP Dashboard's development and chat with the project maintainers and community members. 83 | 84 | - Follow [@osDXP on Twitter](https://twitter.com/osdxp). 85 | - Join [the official Slack room](https://osdxp.slack.com/). 86 | - Reach out to us on our [website](https://osdxp.org) 87 | 88 | ## Versioning 89 | 90 | For transparency into our release cycle and in striving to maintain backward compatibility, osDXP Dashboard is maintained under [the Romantic Versioning guidelines](http://blog.legacyteam.info/2015/12/romver-romantic-versioning/). Sometimes we screw up, but we adhere to those rules whenever possible. 91 | 92 | 93 | ## Changelog 94 | See [the Releases section of our GitHub project](https://github.com/osDXP/osdxp-dashboard/releases) for changelogs for each release version of osDXP Dashboard, 95 | or a historical changelog can be found in the repo, [here](https://github.com/osDXP/osdxp-dashboard/blob/master/CHANGELOG.md). 96 | 97 | 98 | ## License 99 | 100 | osDXP Dashboard is released under the [GPLv2 License](https://github.com/osDXP/osdxp-dashboard/blob/master/LICENSE). -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | The osDXP team and community take security issues in osDXP-Dashboard seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. 4 | 5 | To report a security issue, email [security@osdxp.org](mailto:security@osdxp.org) and include the word "SECURITY" in the subject line. 6 | 7 | We'll endeavor to respond quickly, and will keep you updated throughout the process. -------------------------------------------------------------------------------- /assets/fonts/OSDXPI.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/fonts/OSDXPI.eot -------------------------------------------------------------------------------- /assets/fonts/OSDXPI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/fonts/OSDXPI.ttf -------------------------------------------------------------------------------- /assets/fonts/OSDXPI.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/fonts/OSDXPI.woff -------------------------------------------------------------------------------- /assets/images/OSDXP-logo-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/images/OSDXP-logo-100.png -------------------------------------------------------------------------------- /assets/images/OSDXP-logo-inverse-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/images/OSDXP-logo-inverse-100.png -------------------------------------------------------------------------------- /assets/images/backburger-inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/images/backburger-inverse.png -------------------------------------------------------------------------------- /assets/images/backburger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/images/backburger.png -------------------------------------------------------------------------------- /assets/images/osDXP_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/images/placeholder.png -------------------------------------------------------------------------------- /assets/images/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osDXP/osdxp-dashboard/b1392d29da2609a884ab66d318858f9f2e5e92d1/assets/images/sample.png -------------------------------------------------------------------------------- /assets/js/main.js: -------------------------------------------------------------------------------- 1 | (($) => { 2 | $(() => { 3 | const clearMessages = $wrapper => { 4 | $wrapper.find('.error-messages').remove(); 5 | $wrapper.find('.success-messages').remove(); 6 | }; 7 | 8 | const renderMessages = (response, $wrapper) => { 9 | const messageTypes = ['error', 'success']; 10 | 11 | // Process messages. 12 | Object.keys(messageTypes).forEach(i => { 13 | const messageType = messageTypes[i]; 14 | 15 | if (response[`${messageType}_messages`]) { 16 | const $messages = $(`
`); 17 | 18 | Object.keys(response[`${messageType}_messages`]).forEach(i => { 19 | $messages.append( 20 | $(`
`).text(response[`${messageType}_messages`][i]) 21 | ); 22 | }); 23 | 24 | $wrapper.append($messages); 25 | } 26 | }); 27 | }; 28 | 29 | $('.js-osdxp-submit-module-license').on('click', event => { 30 | event.preventDefault(); 31 | 32 | const pressEnterEvent = $.Event('keypress', { which: 13 }); 33 | $(event.currentTarget).siblings('.js-osdxp-module-license').trigger(pressEnterEvent); 34 | }); 35 | 36 | $('.js-osdxp-module-license').on('keypress', event => { 37 | // Only handle enter key press (code = 13). 38 | if (event.keyCode !== 13 && event.which !== 13) { 39 | return; 40 | } 41 | 42 | event.preventDefault(); 43 | 44 | const $field = $(event.currentTarget); 45 | const $wrapper = $field.parents('.module-license-key'); 46 | const pluginSlug = $field.data('module'); 47 | const licenseKey = $field.val(); 48 | 49 | // Disable field until the request has been complete. 50 | $field.prop('disabled', true); 51 | 52 | // Send license request. 53 | $.ajax(`${osDXPDashboard.restUrl}/license/${pluginSlug}/${licenseKey}`, { 54 | beforeSend: (xhr) => { 55 | xhr.setRequestHeader('X-WP-Nonce', osDXPDashboard.restNonce); 56 | }, 57 | method: 'POST' 58 | }).always(() => { 59 | $field.prop('disabled', false); 60 | }).done((response) => { 61 | // Remove previously set error/success messages. 62 | clearMessages($wrapper); 63 | 64 | if (response.license_key_markup) { 65 | // Add license key markup. 66 | $wrapper.append(response.license_key_markup); 67 | 68 | // Remove any license errors for this plugin. 69 | $(`.${pluginSlug}-license-error`).fadeOut(); 70 | 71 | $wrapper.find('.license-input-wrapper').addClass('hidden'); 72 | } 73 | 74 | renderMessages(response, $wrapper); 75 | }); 76 | }); 77 | 78 | $('.module-license-key').on('click', '.js-osdxp-module-remove-license', event => { 79 | event.preventDefault(); 80 | 81 | // Display a confirmation and only continue if the user clicked "Yes". 82 | if (!confirm(osDXPDashboard.text.licenseKeyRemovalConfirmation)) { 83 | return; 84 | } 85 | 86 | const $button = $(event.currentTarget); 87 | const $wrapper = $button.parents('.module-license-key'); 88 | const pluginSlug = $button.data('module'); 89 | 90 | // Disable field until the request has been complete. 91 | $button.prop('disabled', true); 92 | 93 | // Send license request. 94 | $.ajax(`${osDXPDashboard.restUrl}/license/${pluginSlug}`, { 95 | beforeSend: (xhr) => { 96 | xhr.setRequestHeader('X-WP-Nonce', osDXPDashboard.restNonce); 97 | }, 98 | method: 'DELETE' 99 | }).always(() => { 100 | $button.prop('disabled', false); 101 | }).done((response) => { 102 | // Remove previously set error/success messages. 103 | clearMessages($wrapper); 104 | 105 | if (response.success) { 106 | // Remove the license key markup. 107 | $button.parents('.display-license').remove(); 108 | 109 | // Clear value from license field. 110 | $wrapper.find('.js-osdxp-module-license').val(null); 111 | 112 | // Show license field. 113 | $wrapper.find('.license-input-wrapper').removeClass('hidden'); 114 | 115 | // Remove any license errors for this plugin. 116 | $(`.${pluginSlug}-license-error`).fadeOut(); 117 | } 118 | 119 | renderMessages(response, $wrapper); 120 | }); 121 | }); 122 | 123 | var dxpActions = $('#dxp-actions'); 124 | var dxpActionsContainer = dxpActions.closest('#dxp_actions.postbox'); 125 | var dxpActionsHideLabel = $('label[for="dxp_actions-hide"]'); 126 | dxpActions.prependTo('#dashboard-widgets').addClass('meta-box-sortables no-padding-top'); 127 | dxpActionsContainer.remove(); 128 | dxpActionsHideLabel.remove(); 129 | 130 | $(".dxp-dashboard #wp-admin-bar-root-default li").each(function () { 131 | $(this).addClass('current'); 132 | }); 133 | 134 | $('.plugin-update-tr td').attr('colspan', 4); 135 | }); 136 | })(jQuery); 137 | -------------------------------------------------------------------------------- /assets/js/upload-toggle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @toggle functionality for the upload module node in modules installed page 3 | * 4 | */ 5 | 6 | jQuery( document ).ready( function( $ ) { 7 | $('#osdxp-module-upload-button').click(function(){ 8 | $('#osdxp-module-upload-field').toggle(); 9 | }); 10 | }); -------------------------------------------------------------------------------- /assets/sass/_admin-menu.scss: -------------------------------------------------------------------------------- 1 | #adminmenu { 2 | li.wp-menu-separator { 3 | height: auto; 4 | margin: 10px 0; 5 | } 6 | 7 | div.separator { 8 | background: rgba($color-white, .15); 9 | height: 1px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /assets/sass/_available-modules.scss: -------------------------------------------------------------------------------- 1 | .am-container-base, 2 | .am-pricing, 3 | .am-grid-col-container, 4 | .am-header, 5 | .am-module-footer { 6 | padding: 15px; 7 | } 8 | 9 | .am-header {} 10 | 11 | .available-modules-header { 12 | .check-available-modules { 13 | margin-top: 15px; 14 | vertical-align: text-bottom; 15 | } 16 | } 17 | 18 | .am-grid-container { 19 | display: grid; 20 | grid-gap: 1em; 21 | grid-template-columns: repeat(auto-fill, minmax(450px, 1fr)); 22 | margin: 10px 20px 0 2px; 23 | 24 | @media screen and (max-width: 782px) { 25 | margin-right: 12px; 26 | } 27 | } 28 | 29 | .am-grid-col-container { 30 | display: grid; 31 | grid-gap: 1em; 32 | grid-template-columns: auto auto; 33 | margin: auto; 34 | } 35 | 36 | .am-col { 37 | align-content: space-between; 38 | background-color: $color-white; 39 | display: grid; 40 | grid-template-columns: 1fr; 41 | max-width: 650px; 42 | min-width: 450px; 43 | position: relative; 44 | 45 | &:before { 46 | background: linear-gradient(135deg, $base-color 0%, $color-scarlet 100%); 47 | content: ''; 48 | display: block; 49 | height: 2px; 50 | left: 0; 51 | position: absolute; 52 | right: 0; 53 | top: -2px; 54 | } 55 | } 56 | 57 | .am-pricing div { 58 | margin: 0; 59 | padding-bottom: 5px; 60 | text-align: right; 61 | } 62 | 63 | .am-info { 64 | grid-template-columns: 25% auto; 65 | img { 66 | width: 100%; 67 | } 68 | h3 { 69 | margin: 0; 70 | } 71 | } 72 | 73 | .am-module-footer { 74 | border-top: 2px solid #e5e5e5; 75 | height: 15px; 76 | padding: 2em; 77 | p { 78 | margin: auto; 79 | position: relative; 80 | } 81 | } 82 | 83 | .price { 84 | font-size: 2em; 85 | font-weight: normal; 86 | } 87 | 88 | .certified { 89 | color: $base-color; 90 | font-weight: bold; 91 | } 92 | 93 | .get-module { 94 | background: $base-color; 95 | border-radius: 2px; 96 | color: $color-white; 97 | display: block; 98 | height: 20px; 99 | padding: 8px; 100 | text-align: center; 101 | text-decoration: none; 102 | width: 100px; 103 | } 104 | -------------------------------------------------------------------------------- /assets/sass/_common.scss: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: $color-blackcurrant2; 3 | } 4 | 5 | .postbox { 6 | box-shadow: 0 5px 10px rgba($color-black, .05); 7 | 8 | &:before { 9 | background: linear-gradient(135deg, $base-color 0%, $color-scarlet 100%); 10 | content: ''; 11 | display: block; 12 | height: 2px; 13 | } 14 | } 15 | 16 | .wrap { 17 | .add-new-h2, 18 | .add-new-h2:active, 19 | .page-title-action, 20 | .page-title-action:active { 21 | color: $base-color; 22 | } 23 | } 24 | 25 | .dxp-dashboard .display-license { 26 | color: $color-matterhorn; 27 | } 28 | 29 | .success-message { 30 | color: $color-fruit-salad; 31 | } 32 | 33 | .js-osdxp-module-remove-license.button-primary { 34 | margin-left: 30px; 35 | } 36 | 37 | .osdxp-nav-tabs { 38 | border-bottom: 1px solid $color-very-light-gray; 39 | display: flex; 40 | margin-top: 1em; 41 | 42 | li { 43 | border-bottom: none; 44 | border-radius: 2px 2px 0 0; 45 | border: 1px solid $color-very-light-gray; 46 | font-size: 12px; 47 | height: 34px; 48 | line-height: 34px; 49 | margin: 0 5px 0 0; 50 | 51 | @media (min-width: 480px) { 52 | font-size: 14px; 53 | margin: 0 10px 0 0; 54 | } 55 | 56 | &.active { 57 | background: linear-gradient(180deg, $color-white 0%, $color-white-smoke 100%); 58 | border: none; 59 | box-shadow: inset -1px 0 0 0 $color-very-light-gray, inset 0 2px 0 0 $base-color, inset 1px 0 0 0 $color-very-light-gray; 60 | margin-bottom: -2px; 61 | padding-bottom: 2px; 62 | 63 | a { 64 | color: $base-color; 65 | } 66 | } 67 | 68 | a { 69 | color: $color-charcoal; 70 | display: block; 71 | font-weight: 600; 72 | padding: 0 12px; 73 | text-decoration: none; 74 | 75 | &:active, &:hover { 76 | color: $base-color; 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /assets/sass/_forms.scss: -------------------------------------------------------------------------------- 1 | input[type="text"], 2 | input[type="password"], 3 | input[type="checkbox"], 4 | input[type="color"], 5 | input[type="date"], 6 | input[type="datetime"], 7 | input[type="datetime-local"], 8 | input[type="email"], 9 | input[type="month"], 10 | input[type="number"], 11 | input[type="search"], 12 | input[type="radio"], 13 | input[type="tel"], 14 | input[type="text"], 15 | input[type="time"], 16 | input[type="url"], 17 | input[type="week"], 18 | select, 19 | textarea { 20 | &:focus { 21 | border-color: $base-color; 22 | box-shadow: 0 0 10px rgba($base-color, .2), 1px 1px 5px rgba($color-black, .15) inset; 23 | } 24 | } 25 | 26 | input:disabled, 27 | input.disabled, 28 | select:disabled, 29 | select.disabled, 30 | textarea:disabled, 31 | textarea.disabled { 32 | background: $color-white-smoke; 33 | box-shadow: 1px 1px 5px rgba($color-black, .15); 34 | } 35 | 36 | input#title { 37 | color: $color-blackcurrant2; 38 | } 39 | -------------------------------------------------------------------------------- /assets/sass/_icons.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'OSDXPI'; 3 | src: url('../assets/fonts/OSDXPI.eot?10dxtx'); 4 | src: url('../assets/fonts/OSDXPI.eot?10dxtx#iefix') format('embedded-opentype'), 5 | url('../assets/fonts/OSDXPI.ttf?10dxtx') format('truetype'), 6 | url('../assets/fonts/OSDXPI.woff?10dxtx') format('woff'), 7 | url('../assets/fonts/OSDXPI.svg?10dxtx#OSDXPI') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | [class^="icon-osdxpi-"], [class*=" icon-osdxpi-"] { 13 | /* use !important to prevent issues with browser extensions that change fonts */ 14 | font-family: 'OSDXPI' !important; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | 22 | /* Better Font Rendering =========== */ 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | .icon-osdxpi-responsive:before { 28 | content: "\e913"; 29 | } 30 | .icon-osdxpi-tablet-cellphone:before { 31 | content: "\e914"; 32 | } 33 | .icon-osdxpi-application:before { 34 | content: "\e915"; 35 | } 36 | .icon-osdxpi-calendar-clock:before { 37 | content: "\e916"; 38 | } 39 | .icon-osdxpi-link:before { 40 | content: "\e917"; 41 | } 42 | .icon-osdxpi-account-outline:before { 43 | content: "\e918"; 44 | } 45 | .icon-osdxpi-earth:before { 46 | content: "\e919"; 47 | } 48 | .icon-osdxpi-segmentation:before { 49 | content: "\e900"; 50 | } 51 | .icon-osdxpi-personalization:before { 52 | content: "\e901"; 53 | } 54 | .icon-osdxpi-configuration:before { 55 | content: "\e902"; 56 | } 57 | .icon-osdxpi-forwardburger:before { 58 | content: "\e903"; 59 | } 60 | .icon-osdxpi-backburger:before { 61 | content: "\e904"; 62 | } 63 | .icon-osdxpi-legacy-plugins:before { 64 | content: "\e905"; 65 | } 66 | .icon-osdxpi-themes:before { 67 | content: "\e906"; 68 | } 69 | .icon-osdxpi-sites:before { 70 | content: "\e907"; 71 | } 72 | .icon-osdxpi-tools:before { 73 | content: "\e908"; 74 | } 75 | .icon-osdxpi-users:before { 76 | content: "\e909"; 77 | } 78 | .icon-osdxpi-settings:before { 79 | content: "\e90a"; 80 | } 81 | .icon-osdxpi-3d-cube:before { 82 | content: "\e90b"; 83 | } 84 | .icon-power-plug:before { 85 | content: "\e90c"; 86 | } 87 | .icon-brush:before { 88 | content: "\e90d"; 89 | } 90 | .icon-osdxpi-media:before { 91 | content: "\e90e"; 92 | } 93 | .icon-osdxpi-custom-post-type:before { 94 | content: "\e90f"; 95 | } 96 | .icon-osdxpi-articles-outline:before { 97 | content: "\e910"; 98 | } 99 | .icon-osdxpi-pages-outline:before { 100 | content: "\e911"; 101 | } 102 | .icon-osdxpi-home-outline:before { 103 | content: "\e912"; 104 | } 105 | 106 | 107 | .dashicons-dashboard { 108 | &:before { 109 | font-family: 'OSDXPI'; 110 | content: "\e912"; 111 | } 112 | } 113 | 114 | .dashicons-admin-page { 115 | &:before { 116 | font-family: 'OSDXPI'; 117 | content: "\e911"; 118 | } 119 | } 120 | 121 | .dashicons-admin-post { 122 | &:before { 123 | font-family: 'OSDXPI'; 124 | content: "\e910"; 125 | } 126 | } 127 | 128 | .dashicons-admin-media { 129 | &:before { 130 | font-family: 'OSDXPI'; 131 | content: "\e90e"; 132 | } 133 | } 134 | 135 | .dashicons-admin-appearance { 136 | &:before { 137 | font-family: 'OSDXPI'; 138 | content: "\e90d"; 139 | } 140 | } 141 | 142 | .dashicons-admin-generic { 143 | &:before { 144 | font-family: 'OSDXPI'; 145 | content: "\e90c"; 146 | } 147 | } 148 | 149 | .dashicons-admin-settings { 150 | &:before { 151 | font-family: 'OSDXPI'; 152 | content: "\e90a"; 153 | } 154 | } 155 | 156 | .dashicons-admin-users { 157 | &:before { 158 | font-family: 'OSDXPI'; 159 | content: "\e909"; 160 | } 161 | } 162 | 163 | .dashicons-admin-tools { 164 | &:before { 165 | font-family: 'OSDXPI'; 166 | content: "\e908"; 167 | } 168 | } 169 | 170 | .dashicons-admin-plugins { 171 | &:before { 172 | font-family: 'OSDXPI'; 173 | content: "\e905"; 174 | } 175 | } 176 | .dashicons-admin-multisite { 177 | &:before { 178 | font-family: 'OSDXPI'; 179 | content: "\e907"; 180 | } 181 | } 182 | 183 | .network-admin { 184 | .dashicons-admin-appearance { 185 | &:before { 186 | font-family: 'OSDXPI'; 187 | content: "\e906"; 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /assets/sass/_list-tables.scss: -------------------------------------------------------------------------------- 1 | .tablenav { 2 | margin: 10px 0 15px; 3 | } 4 | 5 | .module-actions { 6 | a { 7 | 8 | width: 134px; 9 | 10 | + a { 11 | margin-top: 10px !important; 12 | } 13 | } 14 | } 15 | 16 | .wp-core-ui .module-actions .button, 17 | .wp-core-ui .module-actions .button-primary, 18 | .wp-core-ui .module-actions .button-secondary { 19 | vertical-align: bottom; 20 | } 21 | 22 | .license-input-wrapper { 23 | button.button-primary { 24 | vertical-align: middle; 25 | } 26 | } 27 | 28 | .wp-list-table { 29 | height: 100%; 30 | } 31 | 32 | 33 | .osdxp-module-description-column { 34 | display: flex; 35 | flex-direction: column; 36 | justify-content: space-between; 37 | overflow: auto; 38 | } 39 | 40 | .toplevel_page_dxp-modules-installed { 41 | .wp-list-table.plugins .plugin-title { 42 | white-space: normal; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /assets/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Button mixin- creates 3d-ish button effect with correct 3 | * highlights/shadows, based on a base color. 4 | */ 5 | @mixin button( $button-color, $text-color: #fff ) { 6 | background: $button-color; 7 | border-color: darken( $button-color, 10% ) darken( $button-color, 15% ) darken( $button-color, 15% ); 8 | color: $text-color; 9 | box-shadow: 0 1px 0 darken( $button-color, 15% ); 10 | text-shadow: 0 -1px 1px darken( $button-color, 15% ), 11 | 1px 0 1px darken( $button-color, 15% ), 12 | 0 1px 1px darken( $button-color, 15% ), 13 | -1px 0 1px darken( $button-color, 15% ); 14 | 15 | &:hover, 16 | &:focus { 17 | background: lighten( $button-color, 3% ); 18 | border-color: darken( $button-color, 15% ); 19 | color: $text-color; 20 | box-shadow: 0 1px 0 darken( $button-color, 15% ); 21 | } 22 | 23 | &:focus { 24 | box-shadow: inset 0 1px 0 darken( $button-color, 10% ), 25 | 0 0 2px 1px #33b3db; 26 | } 27 | 28 | &:active, 29 | &.active, 30 | &.active:focus, 31 | &.active:hover { 32 | background: darken( $button-color, 10% ); 33 | border-color: darken( $button-color, 15% ); 34 | box-shadow: inset 0 2px 0 darken( $button-color, 15% ); 35 | } 36 | 37 | &[disabled], 38 | &:disabled, 39 | &.button-primary-disabled, 40 | &.disabled { 41 | color: hsl( hue( $button-color ), 10%, 80% ) !important; 42 | background: darken( $button-color, 8% ) !important; 43 | border-color: darken( $button-color, 15% ) !important; 44 | text-shadow: none !important; 45 | } 46 | 47 | &.button-hero { 48 | box-shadow: 0 2px 0 darken( $button-color, 15% ) !important; 49 | &:active { 50 | box-shadow: inset 0 3px 0 darken( $button-color, 15% ) !important; 51 | } 52 | } 53 | 54 | } 55 | /* 56 | * Button-secondary mixin- creates 3d-ish button effect with correct 57 | * highlights/shadows, based on a base color. 58 | */ 59 | @mixin button-secondary( $text-color ) { 60 | vertical-align: super; 61 | background: #fff; 62 | border-color: #fff; 63 | color: $text-color; 64 | border: 1px solid #CCCCCC; 65 | border-radius: 2px; 66 | background-color: #FFFFFF; 67 | box-shadow: inset -2px -2px 0 0 rgba(0,0,0,0.1); 68 | &:hover, 69 | &:focus { 70 | color: $text-color; 71 | background: #FAF9FF; 72 | border: 1px solid #4B1BE2; 73 | } 74 | 75 | &:focus { 76 | box-shadow: inset -2px -2px 0 0 rgba(75,27,226,0.15); 77 | } 78 | 79 | &:active, 80 | &.active, 81 | &.active:focus, 82 | &.active:hover { 83 | color: $text-color; 84 | background: #FAF9FF; 85 | border: 1px solid #4B1BE2; 86 | box-shadow: inset -2px -2px 0 0 rgba(75,27,226,0.15); 87 | } 88 | 89 | &[disabled], 90 | &:disabled, 91 | &.button-primary-disabled, 92 | &.disabled { 93 | box-shadow: inset -2px -2px 0 0 rgba(0,0,0,0.1)!important; 94 | color: rgba(136,136,136,0.7)!important; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /assets/sass/_transition.scss: -------------------------------------------------------------------------------- 1 | @keyframes bounceInUp { 2 | from, 3 | 60%, 4 | 75%, 5 | 90%, 6 | to { 7 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 8 | } 9 | 10 | from { 11 | opacity: 0; 12 | transform: translate3d(0, 3000px, 0); 13 | } 14 | 15 | 60% { 16 | opacity: 1; 17 | transform: translate3d(0, -20px, 0); 18 | } 19 | 20 | 75% { 21 | transform: translate3d(0, 10px, 0); 22 | } 23 | 24 | 90% { 25 | transform: translate3d(0, -5px, 0); 26 | } 27 | 28 | to { 29 | transform: translate3d(0, 0, 0); 30 | } 31 | } 32 | 33 | @keyframes bounceInDown { 34 | from, 35 | 60%, 36 | 75%, 37 | 90%, 38 | to { 39 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 40 | } 41 | 42 | 0% { 43 | opacity: 0; 44 | transform: translate3d(0, -3000px, 0); 45 | } 46 | 47 | 60% { 48 | opacity: 1; 49 | transform: translate3d(0, 25px, 0); 50 | } 51 | 52 | 75% { 53 | transform: translate3d(0, -10px, 0); 54 | } 55 | 56 | 90% { 57 | transform: translate3d(0, 5px, 0); 58 | } 59 | 60 | to { 61 | transform: translate3d(0, 0, 0); 62 | } 63 | } 64 | 65 | @keyframes fadeIn { 66 | from { 67 | opacity: 0; 68 | } 69 | 70 | to { 71 | opacity: 1; 72 | } 73 | } 74 | 75 | .dxp-transition-in #adminmenuwrap, .dxp-transition-in #adminmenuback{ 76 | animation-name: bounceInDown; 77 | animation-duration: .7s; 78 | animation-fill-mode: both; 79 | } 80 | 81 | .dxp-transition-out #adminmenuwrap, .dxp-transition-out #adminmenuback{ 82 | animation-name: bounceInUp; 83 | animation-duration: .7s; 84 | animation-fill-mode: both; 85 | } 86 | -------------------------------------------------------------------------------- /assets/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | // assign default value to all undefined variables 2 | 3 | 4 | // core variables 5 | 6 | $color-black: #000 !default; 7 | $color-blackcurrant: #281F2E !default; 8 | $color-blackcurrant2: #110034 !default; 9 | $color-charcoal: #444444 !default; 10 | $color-dark-gray: #B0B0B0 !default; 11 | $color-deep-sky-blue: #00D7FF !default; 12 | $color-fog: #D6BCF8 !default; 13 | $color-fruit-salad: #46b450 !default; 14 | $color-ghost-white: #FAF9FF !default; 15 | $color-matterhorn: #4D4D4D !default; 16 | $color-persian-red: #DC3232 !default; 17 | $color-scarlet: #E81818 !default; 18 | $color-ultramarine: #25048B !default; 19 | $color-very-light-gray: #CCCCCC !default; 20 | $color-white: #fff !default; 21 | $color-white-smoke: #F4F4F4 !default; 22 | 23 | $text-color: $color-white !default; 24 | $base-color: #4B1BE2 !default; 25 | $icon-color: $color-white !default; 26 | $highlight-color: $color-white !default; 27 | $notification-color: #d54e21 !default; 28 | $base-color-secondary: #E81818 !default; 29 | 30 | // Media Query Ranges 31 | $small-screen-up: 783px !default; 32 | $small-screen: 782px !default; 33 | $small-and-down: "only screen and (max-width : #{$small-screen})" !default; 34 | 35 | // global 36 | 37 | $body-background: #f1f1f1 !default; 38 | 39 | $link: $base-color !default; 40 | $link-focus: $color-ultramarine !default; 41 | 42 | $button-color: $base-color !default; 43 | $form-checked: $base-color !default; 44 | 45 | 46 | // admin menu & admin-bar 47 | 48 | $menu-text: $text-color !default; 49 | $menu-icon: $icon-color !default; 50 | $menu-background: #3C15B4 !default; 51 | 52 | $menu-highlight-text: $base-color !default; 53 | $menu-highlight-icon: $base-color !default; 54 | $menu-highlight-background: $text-color !default; 55 | 56 | $menu-current-text: $menu-highlight-text !default; 57 | $menu-current-icon: $menu-highlight-icon !default; 58 | $menu-current-background: $menu-highlight-background !default; 59 | 60 | $menu-submenu-text: rgba($text-color, .8) !default; 61 | $menu-submenu-background: $link-focus !default; 62 | $menu-submenu-background-alt: desaturate( lighten( $menu-background, 7% ), 7% ) !default; 63 | 64 | $menu-submenu-focus-text: $highlight-color !default; 65 | $menu-submenu-current-text: $text-color !default; 66 | 67 | $menu-bubble-text: $menu-background !default; 68 | $menu-bubble-background: #49D9FF !default; 69 | $menu-bubble-current-text: $menu-bubble-text !default; 70 | $menu-bubble-current-background: $menu-bubble-background !default; 71 | 72 | $menu-collapse-text: $menu-icon !default; 73 | $menu-collapse-icon: $menu-icon !default; 74 | $menu-collapse-focus-text: $text-color !default; 75 | $menu-collapse-focus-icon: $menu-highlight-icon !default; 76 | 77 | $adminbar-background: $color-white !default; 78 | $adminbar-text: $color-blackcurrant !default; 79 | $adminbar-height: 44px !default; 80 | 81 | $adminbar-avatar-frame: lighten( $menu-background, 7% ) !default; 82 | $adminbar-input-background: lighten( $menu-background, 7% ) !default; 83 | 84 | $adminbar-recovery-exit-text: $menu-bubble-text !default; 85 | $adminbar-recovery-exit-background: $menu-bubble-background !default; 86 | $adminbar-recovery-exit-background-alt: mix(black, $adminbar-recovery-exit-background, 10%) !default; 87 | 88 | $menu-customizer-text: mix( $base-color, $text-color, 40% ) !default; 89 | -------------------------------------------------------------------------------- /assets/sass/_welcome-panel.scss: -------------------------------------------------------------------------------- 1 | #dxp-actions { 2 | padding: 0 0 40px 0; 3 | 4 | .title { 5 | display: block; 6 | margin-top: 30px; 7 | } 8 | 9 | .row { 10 | align-items: stretch; 11 | display: flex; 12 | flex-direction: row; 13 | flex-wrap: wrap; 14 | justify-content: stretch; 15 | margin: 0 -8px; 16 | min-width: 100%; 17 | 18 | .col { 19 | margin-bottom: 16px; 20 | min-width: 100px; 21 | text-decoration: none; 22 | width: 20%; 23 | 24 | &:hover { 25 | .postbox { 26 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.15); 27 | } 28 | } 29 | } 30 | 31 | .postbox { 32 | display: flex; 33 | flex-direction: column; 34 | justify-content: stretch; 35 | margin: 0 8px; 36 | min-height: 100%; 37 | min-width: auto; 38 | 39 | > div { 40 | align-items: flex-end; 41 | display: flex; 42 | flex-direction: row; 43 | flex: 1; 44 | justify-content: stretch; 45 | padding: 30px; 46 | } 47 | 48 | [class*=dashicons]:before { 49 | font-size: 40px; 50 | height: 40px; 51 | margin-bottom: 10px; 52 | width: 40px; 53 | } 54 | 55 | .group { 56 | flex: 1; 57 | } 58 | 59 | span { 60 | color: $color-blackcurrant; 61 | display: block; 62 | font-size: 10px; 63 | line-height: 1.2; 64 | margin-bottom: 5px; 65 | text-transform: uppercase; 66 | } 67 | 68 | p { 69 | color: $color-blackcurrant; 70 | font-size: 18px; 71 | font-weight: 500; 72 | line-height: 1.2; 73 | margin: 0; 74 | } 75 | 76 | .button { 77 | margin-left: 20px; 78 | width: max-content; 79 | } 80 | } 81 | 82 | &.large { 83 | .col { 84 | width: 33.33%; 85 | } 86 | } 87 | } 88 | } 89 | 90 | @media #{$small-and-down} { 91 | #dxp-actions { 92 | padding: 0 0 20px 0; 93 | 94 | .row, 95 | .row.large { 96 | .col { 97 | max-width: 100%; 98 | width: 100%; 99 | 100 | .postbox { 101 | > div { 102 | padding: 15px 20px; 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /assets/sass/style-admin.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import 'mixins'; 3 | @import 'transition'; 4 | @import 'admin-bar'; 5 | 6 | @import "admin"; 7 | @import "admin-menu"; 8 | @import "common"; 9 | @import "forms"; 10 | @import "list-tables"; 11 | @import "available-modules"; 12 | @import "icons"; 13 | @import "welcome-panel"; 14 | 15 | //@TODO: REMOVE THIS AND ADD MY SITES AS SUBMENU; uncomment lines in admin-bar.scss 16 | #wp-admin-bar-my-sites { 17 | color: $base-color!important; 18 | 19 | & > a:before { 20 | content:none !important; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /assets/sass/style-wp-admin.scss: -------------------------------------------------------------------------------- 1 | @import 'transition'; 2 | @import 'wp-admin-bar'; 3 | -------------------------------------------------------------------------------- /assets/sass/wp-admin-bar.scss: -------------------------------------------------------------------------------- 1 | body:not(.dxp-dashboard) { 2 | #wp-admin-bar-switch-to-dxp-backburger a > div:before { 3 | background: url("../assets/images/backburger-inverse.png") no-repeat center center; 4 | content: ''; 5 | display: inline-block; 6 | height: 32px; 7 | width: 20px; 8 | } 9 | 10 | #wpadminbar { 11 | #wp-admin-bar-switch-to-dxp a, 12 | &:not(.mobile) .ab-top-menu > #wp-admin-bar-switch-to-dxp > .ab-item:focus, 13 | &.nojq .quicklinks .ab-top-menu > #wp-admin-bar-switch-to-dxp > .ab-item:focus, 14 | &:not(.mobile) .ab-top-menu > #wp-admin-bar-switch-to-dxp:hover > .ab-item, 15 | .ab-top-menu > #wp-admin-bar-switch-to-dxp.hover > .ab-item { 16 | background: transparent url(../assets/images/OSDXP-logo-inverse-100.png) 10px 50% no-repeat; 17 | background-size: 80px auto; 18 | color: transparent; 19 | display: block; 20 | font: 0/0 a; 21 | text-shadow: none; 22 | width: 80px; 23 | } 24 | } 25 | 26 | #wp-toolbar > ul > #wp-admin-bar-switch-to-dxp { 27 | display: block; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /build/admin-bar.min.css: -------------------------------------------------------------------------------- 1 | #wpadminbar *{line-height:44px}#wpadminbar #wp-admin-bar-site-name a.ab-item{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#wpadminbar{background:#fff;box-shadow:0 5px 10px 0 rgba(0,0,0,.05);color:#281f2e;height:44px;line-height:44px}#wpadminbar .quicklinks .ab-empty-item,#wpadminbar .quicklinks a,#wpadminbar .shortlink-input{height:44px}#wpadminbar.ie7 .menupop .ab-sub-wrapper,#wpadminbar.ie7 .shortlink-input{top:44px}@media screen and (min-width:782px){#wpadminbar .menupop li.hover>.ab-sub-wrapper,#wpadminbar .menupop li:hover>.ab-sub-wrapper{margin-top:-44px}}#wpadminbar .ab-icon,#wpadminbar .ab-item:before,#wpadminbar>#wp-toolbar>#wp-admin-bar-root-default .ab-icon,.wp-admin-bar-arrow{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;height:44px;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;padding:0}#wpadminbar .ab-label{height:44px}#wpadminbar .quicklinks .ab-sub-wrapper .menupop.hover>a,#wpadminbar .quicklinks .menupop ul li a:focus,#wpadminbar .quicklinks .menupop ul li a:focus strong,#wpadminbar .quicklinks .menupop ul li a:hover,#wpadminbar .quicklinks .menupop ul li a:hover strong,#wpadminbar .quicklinks .menupop.hover ul li a:focus,#wpadminbar .quicklinks .menupop.hover ul li a:hover,#wpadminbar .quicklinks .menupop.hover ul li div[tabindex]:focus,#wpadminbar .quicklinks .menupop.hover ul li div[tabindex]:hover,#wpadminbar li #adminbarsearch.adminbar-focused:before,#wpadminbar li:hover #adminbarsearch:before,#wpadminbar.nojs .quicklinks .menupop:hover ul li a:focus,#wpadminbar.nojs .quicklinks .menupop:hover ul li a:hover{color:#fff}#wpadminbar #wp-admin-bar-site-name>.ab-item:before{content:"\f541";top:2px}#wpadminbar.ie8 #wp-admin-bar-search{min-width:44px}#wpadminbar #adminbarsearch{height:44px}.no-font-face #wpadminbar #wp-admin-bar-edit>.ab-item,.no-font-face #wpadminbar #wp-admin-bar-site-name>.ab-item{text-indent:0}@media screen and (max-width:782px){#wpadminbar *{line-height:44px}#wpadminbar #wp-admin-bar-my-account a.ab-item,#wpadminbar #wp-admin-bar-site-name a.ab-item{text-overflow:clip}#wpadminbar #wp-admin-bar-customize>.ab-item,#wpadminbar #wp-admin-bar-edit>.ab-item,#wpadminbar #wp-admin-bar-my-account>.ab-item,#wpadminbar #wp-admin-bar-site-name>.ab-item{text-indent:100%;white-space:nowrap;overflow:hidden;width:52px;padding:0;color:#a0a5aa;position:relative}#wpadminbar #wp-admin-bar-customize>.ab-item:before,#wpadminbar #wp-admin-bar-edit>.ab-item:before,#wpadminbar #wp-admin-bar-my-account>.ab-item:before,#wpadminbar #wp-admin-bar-site-name>.ab-item:before{display:block;text-indent:0;font:normal 44px dashicons;speak:none;top:0;width:52px;text-align:center;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpadminbar li#wp-admin-bar-comments,#wpadminbar li#wp-admin-bar-customize,#wpadminbar li#wp-admin-bar-edit,#wpadminbar li#wp-admin-bar-menu-toggle,#wpadminbar li#wp-admin-bar-my-account,#wpadminbar li#wp-admin-bar-new-content,#wpadminbar li#wp-admin-bar-site-name,#wpadminbar li#wp-admin-bar-updates,#wpadminbar li#wp-admin-bar-wp-logo{display:block}#wpadminbar #wp-admin-bar-comments,#wpadminbar #wp-admin-bar-edit,#wpadminbar #wp-admin-bar-my-account,#wpadminbar #wp-admin-bar-new-content,#wpadminbar #wp-admin-bar-site-name,#wpadminbar #wp-admin-bar-updates,#wpadminbar #wp-admin-bar-wp-logo,#wpadminbar .ab-top-menu,#wpadminbar .ab-top-secondary{position:static}}#wpadminbar .ab-item,#wpadminbar a.ab-item,#wpadminbar>#wp-toolbar span.ab-label,#wpadminbar>#wp-toolbar span.noticon{color:#281f2e}#wpadminbar .ab-icon,#wpadminbar .ab-icon:before,#wpadminbar .ab-item:after,#wpadminbar .ab-item:before{color:#281f2e}#wpadminbar .ab-top-menu>li.hover>.ab-item,#wpadminbar .ab-top-menu>li.menupop.hover>.ab-item,#wpadminbar.nojq .quicklinks .ab-top-menu>li>.ab-item:focus,#wpadminbar.nojs .ab-top-menu>li.menupop:hover>.ab-item,#wpadminbar:not(.mobile) .ab-top-menu>li:hover>.ab-item,#wpadminbar:not(.mobile) .ab-top-menu>li>.ab-item:focus{background:#fff;color:#281f2e}#wpadminbar:not(.mobile)>#wp-toolbar a:focus span.ab-label,#wpadminbar:not(.mobile)>#wp-toolbar li.hover span.ab-label,#wpadminbar:not(.mobile)>#wp-toolbar li:hover span.ab-label{color:#281f2e}#wpadminbar:not(.mobile) li:hover #adminbarsearch:before,#wpadminbar:not(.mobile) li:hover .ab-icon:before,#wpadminbar:not(.mobile) li:hover .ab-item:after,#wpadminbar:not(.mobile) li:hover .ab-item:before{color:#281f2e}#wpadminbar .menupop .ab-sub-wrapper{background:#25048b}#wpadminbar .quicklinks .menupop ul.ab-sub-secondary,#wpadminbar .quicklinks .menupop ul.ab-sub-secondary .ab-submenu{background:#4b21cc}#wpadminbar .ab-submenu .ab-item,#wpadminbar .quicklinks .menupop ul li a,#wpadminbar .quicklinks .menupop.hover ul li a,#wpadminbar.nojs .quicklinks .menupop:hover ul li a{color:rgba(255,255,255,.8)}#wpadminbar .menupop .menupop>.ab-item:before,#wpadminbar .quicklinks li .blavatar{color:#281f2e}#wpadminbar .menupop .menupop>.ab-item:hover:before,#wpadminbar .quicklinks .ab-sub-wrapper .menupop.hover>a .blavatar,#wpadminbar .quicklinks li a:focus .blavatar,#wpadminbar .quicklinks li a:hover .blavatar,#wpadminbar.mobile .quicklinks .ab-icon:before,#wpadminbar.mobile .quicklinks .ab-item:before{color:#281f2e}#wpadminbar.mobile .quicklinks .hover .ab-icon:before,#wpadminbar.mobile .quicklinks .hover .ab-item:before{color:#281f2e}#wpadminbar #adminbarsearch:before{color:#281f2e;top:12px}#wpadminbar>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input:focus{background:#fff;color:#281f2e}#wpadminbar #wp-admin-bar-recovery-mode{background-color:#49d9ff;color:#3c15b4}#wpadminbar #wp-admin-bar-recovery-mode .ab-item,#wpadminbar #wp-admin-bar-recovery-mode a.ab-item{color:#3c15b4}#wpadminbar .ab-top-menu>#wp-admin-bar-recovery-mode.hover>.ab-item,#wpadminbar.nojq .quicklinks .ab-top-menu>#wp-admin-bar-recovery-mode>.ab-item:focus,#wpadminbar:not(.mobile) .ab-top-menu>#wp-admin-bar-recovery-mode:hover>.ab-item,#wpadminbar:not(.mobile) .ab-top-menu>#wp-admin-bar-recovery-mode>.ab-item:focus{background-color:#42c3e6;color:#3c15b4}#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar>a img{background-color:#4719d4;border-color:#4719d4}#wpadminbar #wp-admin-bar-user-info .display-name{color:#fff}#wpadminbar #wp-admin-bar-user-info a:hover .display-name{color:#fff}#wpadminbar #wp-admin-bar-user-info .username{color:rgba(255,255,255,.8)}#wp-admin-bar-site-name{top:0;-webkit-transition:top ease-out .4s;transition:top ease-out .4s}#wpadminbar .quicklinks>ul>#wp-admin-bar-switch-to-dxp{display:block}#wpadminbar .quicklinks>ul>#wp-admin-bar-switch-to-dxp>a{padding:0;width:36px}#wp-admin-bar-switch-to-dxp a{overflow:hidden}#wp-admin-bar-switch-to-dxp a>div{white-space:nowrap;width:36px}#wp-admin-bar-switch-to-dxp a>div:before{background:url(../assets/images/backburger.png) no-repeat center center;content:'';display:inline-block;height:44px;width:36px}#wp-admin-bar-switch-to-dxp a>div div{color:#4b1be2;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;font-weight:700;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;margin-top:44px;-webkit-transition:margin ease-in .5s;transition:margin ease-in .5s;white-space:nowrap;width:124px}#wp-admin-bar-switch-to-dxp a br,#wp-admin-bar-switch-to-dxp a div{line-height:normal}#wp-admin-bar-switch-to-dxp:active a>div,#wp-admin-bar-switch-to-dxp:focus a>div,#wp-admin-bar-switch-to-dxp:hover a>div{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:auto}#wp-admin-bar-switch-to-dxp:active a>div div,#wp-admin-bar-switch-to-dxp:focus a>div div,#wp-admin-bar-switch-to-dxp:hover a>div div{margin:0}#wpadminbar .quicklinks>ul>#wp-admin-bar-switch-to-dxp:active>a,#wpadminbar .quicklinks>ul>#wp-admin-bar-switch-to-dxp:focus>a,#wpadminbar .quicklinks>ul>#wp-admin-bar-switch-to-dxp:hover>a{width:auto}#wp-admin-bar-switch-to-dxp:active~#wp-admin-bar-site-name,#wp-admin-bar-switch-to-dxp:focus~#wp-admin-bar-site-name,#wp-admin-bar-switch-to-dxp:hover~#wp-admin-bar-site-name{left:36px;position:absolute;top:-44px}@media screen and (max-width:782px){#wp-admin-bar-switch-to-dxp:active~#wp-admin-bar-site-name,#wp-admin-bar-switch-to-dxp:focus~#wp-admin-bar-site-name,#wp-admin-bar-switch-to-dxp:hover~#wp-admin-bar-site-name{left:89px}}#wpadminbar #wp-admin-bar-site-name>.ab-item:first-child{background:url(../assets/images/osDXP_logo.svg) no-repeat center center;text-indent:-9999px;background-size:100px auto}#wpadminbar #wp-admin-bar-site-name>.ab-item:first-child:before{display:none}#wpadminbar .quicklinks>ul>#wp-admin-bar-site-name>.ab-item:first-child{padding:0;width:124px}#wpadminbar #wp-admin-bar-site-name.hover>.ab-sub-wrapper,#wpadminbar #wp-admin-bar-site-name:hover>.ab-sub-wrapper{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}#wpadminbar #wp-admin-bar-site-name.hover>.ab-sub-wrapper>ul,#wpadminbar #wp-admin-bar-site-name:hover>.ab-sub-wrapper>ul{-webkit-box-ordinal-group:11;-webkit-order:10;-ms-flex-order:10;order:10}#wpadminbar #wp-admin-bar-site-name.hover>.ab-sub-wrapper #wp-admin-bar-site-name-default,#wpadminbar #wp-admin-bar-site-name:hover>.ab-sub-wrapper #wp-admin-bar-site-name-default{-webkit-box-ordinal-group:1;-webkit-order:0;-ms-flex-order:0;order:0}#wpadminbar #wp-admin-bar-site-name.hover>.ab-sub-wrapper #wp-admin-bar-my-sites-list~#wp-admin-bar-appearance,#wpadminbar #wp-admin-bar-site-name:hover>.ab-sub-wrapper #wp-admin-bar-my-sites-list~#wp-admin-bar-appearance{border-bottom:1px solid #4b1be2;-webkit-box-ordinal-group:2;-webkit-order:1;-ms-flex-order:1;order:1}.wp-admin #wpadminbar #wp-admin-bar-site-name #wp-admin-bar-my-sites-list~#wp-admin-bar-site-name-default{border-bottom:1px solid #4b1be2}#wpadminbar .ab-top-menu>li>a.ab-item>.ab-item{background-position:center;height:44px!important} -------------------------------------------------------------------------------- /build/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (function ($) { 4 | $(function () { 5 | var clearMessages = function clearMessages($wrapper) { 6 | $wrapper.find('.error-messages').remove(); 7 | $wrapper.find('.success-messages').remove(); 8 | }; 9 | 10 | var renderMessages = function renderMessages(response, $wrapper) { 11 | var messageTypes = ['error', 'success']; // Process messages. 12 | 13 | Object.keys(messageTypes).forEach(function (i) { 14 | var messageType = messageTypes[i]; 15 | 16 | if (response["".concat(messageType, "_messages")]) { 17 | var $messages = $("
")); 18 | Object.keys(response["".concat(messageType, "_messages")]).forEach(function (i) { 19 | $messages.append($("
")).text(response["".concat(messageType, "_messages")][i])); 20 | }); 21 | $wrapper.append($messages); 22 | } 23 | }); 24 | }; 25 | 26 | $('.js-osdxp-submit-module-license').on('click', function (event) { 27 | event.preventDefault(); 28 | var pressEnterEvent = $.Event('keypress', { 29 | which: 13 30 | }); 31 | $(event.currentTarget).siblings('.js-osdxp-module-license').trigger(pressEnterEvent); 32 | }); 33 | $('.js-osdxp-module-license').on('keypress', function (event) { 34 | // Only handle enter key press (code = 13). 35 | if (event.keyCode !== 13 && event.which !== 13) { 36 | return; 37 | } 38 | 39 | event.preventDefault(); 40 | var $field = $(event.currentTarget); 41 | var $wrapper = $field.parents('.module-license-key'); 42 | var pluginSlug = $field.data('module'); 43 | var licenseKey = $field.val(); // Disable field until the request has been complete. 44 | 45 | $field.prop('disabled', true); // Send license request. 46 | 47 | $.ajax("".concat(osDXPDashboard.restUrl, "/license/").concat(pluginSlug, "/").concat(licenseKey), { 48 | beforeSend: function beforeSend(xhr) { 49 | xhr.setRequestHeader('X-WP-Nonce', osDXPDashboard.restNonce); 50 | }, 51 | method: 'POST' 52 | }).always(function () { 53 | $field.prop('disabled', false); 54 | }).done(function (response) { 55 | // Remove previously set error/success messages. 56 | clearMessages($wrapper); 57 | 58 | if (response.license_key_markup) { 59 | // Add license key markup. 60 | $wrapper.append(response.license_key_markup); // Remove any license errors for this plugin. 61 | 62 | $(".".concat(pluginSlug, "-license-error")).fadeOut(); 63 | $wrapper.find('.license-input-wrapper').addClass('hidden'); 64 | } 65 | 66 | renderMessages(response, $wrapper); 67 | }); 68 | }); 69 | $('.module-license-key').on('click', '.js-osdxp-module-remove-license', function (event) { 70 | event.preventDefault(); // Display a confirmation and only continue if the user clicked "Yes". 71 | 72 | if (!confirm(osDXPDashboard.text.licenseKeyRemovalConfirmation)) { 73 | return; 74 | } 75 | 76 | var $button = $(event.currentTarget); 77 | var $wrapper = $button.parents('.module-license-key'); 78 | var pluginSlug = $button.data('module'); // Disable field until the request has been complete. 79 | 80 | $button.prop('disabled', true); // Send license request. 81 | 82 | $.ajax("".concat(osDXPDashboard.restUrl, "/license/").concat(pluginSlug), { 83 | beforeSend: function beforeSend(xhr) { 84 | xhr.setRequestHeader('X-WP-Nonce', osDXPDashboard.restNonce); 85 | }, 86 | method: 'DELETE' 87 | }).always(function () { 88 | $button.prop('disabled', false); 89 | }).done(function (response) { 90 | // Remove previously set error/success messages. 91 | clearMessages($wrapper); 92 | 93 | if (response.success) { 94 | // Remove the license key markup. 95 | $button.parents('.display-license').remove(); // Clear value from license field. 96 | 97 | $wrapper.find('.js-osdxp-module-license').val(null); // Show license field. 98 | 99 | $wrapper.find('.license-input-wrapper').removeClass('hidden'); // Remove any license errors for this plugin. 100 | 101 | $(".".concat(pluginSlug, "-license-error")).fadeOut(); 102 | } 103 | 104 | renderMessages(response, $wrapper); 105 | }); 106 | }); 107 | var dxpActions = $('#dxp-actions'); 108 | var dxpActionsContainer = dxpActions.closest('#dxp_actions.postbox'); 109 | var dxpActionsHideLabel = $('label[for="dxp_actions-hide"]'); 110 | dxpActions.prependTo('#dashboard-widgets').addClass('meta-box-sortables no-padding-top'); 111 | dxpActionsContainer.remove(); 112 | dxpActionsHideLabel.remove(); 113 | $(".dxp-dashboard #wp-admin-bar-root-default li").each(function () { 114 | $(this).addClass('current'); 115 | }); 116 | $('.plugin-update-tr td').attr('colspan', 4); 117 | }); 118 | })(jQuery); 119 | /** 120 | * @toggle functionality for the upload module node in modules installed page 121 | * 122 | */ 123 | 124 | 125 | jQuery(document).ready(function ($) { 126 | $('#osdxp-module-upload-button').click(function () { 127 | $('#osdxp-module-upload-field').toggle(); 128 | }); 129 | }); 130 | //# sourceMappingURL=app.js.map 131 | -------------------------------------------------------------------------------- /build/app.min.js: -------------------------------------------------------------------------------- 1 | "use strict";!function(r){r(function(){function c(e){e.find(".error-messages").remove(),e.find(".success-messages").remove()}function t(o,a){var c=["error","success"];Object.keys(c).forEach(function(e){var s=c[e];if(o["".concat(s,"_messages")]){var n=r('
'));Object.keys(o["".concat(s,"_messages")]).forEach(function(e){n.append(r('
')).text(o["".concat(s,"_messages")][e]))}),a.append(n)}})}r(".js-osdxp-submit-module-license").on("click",function(e){e.preventDefault();var s=r.Event("keypress",{which:13});r(e.currentTarget).siblings(".js-osdxp-module-license").trigger(s)}),r(".js-osdxp-module-license").on("keypress",function(e){if(13===e.keyCode||13===e.which){e.preventDefault();var s=r(e.currentTarget),n=s.parents(".module-license-key"),o=s.data("module"),a=s.val();s.prop("disabled",!0),r.ajax("".concat(osDXPDashboard.restUrl,"/license/").concat(o,"/").concat(a),{beforeSend:function(e){e.setRequestHeader("X-WP-Nonce",osDXPDashboard.restNonce)},method:"POST"}).always(function(){s.prop("disabled",!1)}).done(function(e){c(n),e.license_key_markup&&(n.append(e.license_key_markup),r(".".concat(o,"-license-error")).fadeOut(),n.find(".license-input-wrapper").addClass("hidden")),t(e,n)})}}),r(".module-license-key").on("click",".js-osdxp-module-remove-license",function(e){if(e.preventDefault(),confirm(osDXPDashboard.text.licenseKeyRemovalConfirmation)){var s=r(e.currentTarget),n=s.parents(".module-license-key"),o=s.data("module");s.prop("disabled",!0),r.ajax("".concat(osDXPDashboard.restUrl,"/license/").concat(o),{beforeSend:function(e){e.setRequestHeader("X-WP-Nonce",osDXPDashboard.restNonce)},method:"DELETE"}).always(function(){s.prop("disabled",!1)}).done(function(e){c(n),e.success&&(s.parents(".display-license").remove(),n.find(".js-osdxp-module-license").val(null),n.find(".license-input-wrapper").removeClass("hidden"),r(".".concat(o,"-license-error")).fadeOut()),t(e,n)})}});var e=r("#dxp-actions"),s=e.closest("#dxp_actions.postbox"),n=r('label[for="dxp_actions-hide"]');e.prependTo("#dashboard-widgets").addClass("meta-box-sortables no-padding-top"),s.remove(),n.remove(),r(".dxp-dashboard #wp-admin-bar-root-default li").each(function(){r(this).addClass("current")}),r(".plugin-update-tr td").attr("colspan",4)})}(jQuery),jQuery(document).ready(function(e){e("#osdxp-module-upload-button").click(function(){e("#osdxp-module-upload-field").toggle()})}); -------------------------------------------------------------------------------- /build/notifications.js: -------------------------------------------------------------------------------- 1 | /* global osdxpNotificationsSettings, jQuery */ 2 | (function( $ ) { 3 | 'use strict'; 4 | 5 | const closet = $( osdxpNotificationsSettings.selectorDrawer ); 6 | const osdxpNotice = $( osdxpNotificationsSettings.selectorNotice ); 7 | const inlineClass = 'osdxp-notice-inline'; 8 | const notices = closet.find( 'div.error,div.notice,div.updated' ); 9 | 10 | function reveal() { 11 | notices.filter( `.${inlineClass}` ).removeClass( `inline ${inlineClass}` ); 12 | closet.insertAfter( osdxpNotice ).show(); 13 | osdxpNotice.remove(); 14 | } 15 | 16 | function getClass() { 17 | if ( closet.find( 'div.error,div.notice-error' ).length ) { 18 | return 'notice-error'; 19 | } 20 | 21 | if ( closet.find( 'div.notice-warning' ).length ) { 22 | return 'notice-warning'; 23 | } 24 | 25 | return 'notice-info'; 26 | } 27 | 28 | if ( notices.length < osdxpNotificationsSettings.threshold ) { 29 | reveal(); 30 | 31 | return; 32 | } 33 | 34 | notices.not( '.inline' ).addClass( `inline ${inlineClass}` ); 35 | 36 | osdxpNotice.addClass( getClass() ).removeClass( 'hide-if-js' ); 37 | 38 | osdxpNotice.find( 'button' ).click( reveal ); 39 | })( jQuery ); 40 | -------------------------------------------------------------------------------- /build/style-wp-admin.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes bounceInUp { 2 | from, 3 | 60%, 4 | 75%, 5 | 90%, 6 | to { 7 | -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 8 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 9 | from { 10 | opacity: 0; 11 | -webkit-transform: translate3d(0, 3000px, 0); 12 | transform: translate3d(0, 3000px, 0); } 13 | 60% { 14 | opacity: 1; 15 | -webkit-transform: translate3d(0, -20px, 0); 16 | transform: translate3d(0, -20px, 0); } 17 | 75% { 18 | -webkit-transform: translate3d(0, 10px, 0); 19 | transform: translate3d(0, 10px, 0); } 20 | 90% { 21 | -webkit-transform: translate3d(0, -5px, 0); 22 | transform: translate3d(0, -5px, 0); } 23 | to { 24 | -webkit-transform: translate3d(0, 0, 0); 25 | transform: translate3d(0, 0, 0); } } 26 | 27 | @keyframes bounceInUp { 28 | from, 29 | 60%, 30 | 75%, 31 | 90%, 32 | to { 33 | -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 34 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 35 | from { 36 | opacity: 0; 37 | -webkit-transform: translate3d(0, 3000px, 0); 38 | transform: translate3d(0, 3000px, 0); } 39 | 60% { 40 | opacity: 1; 41 | -webkit-transform: translate3d(0, -20px, 0); 42 | transform: translate3d(0, -20px, 0); } 43 | 75% { 44 | -webkit-transform: translate3d(0, 10px, 0); 45 | transform: translate3d(0, 10px, 0); } 46 | 90% { 47 | -webkit-transform: translate3d(0, -5px, 0); 48 | transform: translate3d(0, -5px, 0); } 49 | to { 50 | -webkit-transform: translate3d(0, 0, 0); 51 | transform: translate3d(0, 0, 0); } } 52 | 53 | @-webkit-keyframes bounceInDown { 54 | from, 55 | 60%, 56 | 75%, 57 | 90%, 58 | to { 59 | -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 60 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 61 | 0% { 62 | opacity: 0; 63 | -webkit-transform: translate3d(0, -3000px, 0); 64 | transform: translate3d(0, -3000px, 0); } 65 | 60% { 66 | opacity: 1; 67 | -webkit-transform: translate3d(0, 25px, 0); 68 | transform: translate3d(0, 25px, 0); } 69 | 75% { 70 | -webkit-transform: translate3d(0, -10px, 0); 71 | transform: translate3d(0, -10px, 0); } 72 | 90% { 73 | -webkit-transform: translate3d(0, 5px, 0); 74 | transform: translate3d(0, 5px, 0); } 75 | to { 76 | -webkit-transform: translate3d(0, 0, 0); 77 | transform: translate3d(0, 0, 0); } } 78 | 79 | @keyframes bounceInDown { 80 | from, 81 | 60%, 82 | 75%, 83 | 90%, 84 | to { 85 | -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 86 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 87 | 0% { 88 | opacity: 0; 89 | -webkit-transform: translate3d(0, -3000px, 0); 90 | transform: translate3d(0, -3000px, 0); } 91 | 60% { 92 | opacity: 1; 93 | -webkit-transform: translate3d(0, 25px, 0); 94 | transform: translate3d(0, 25px, 0); } 95 | 75% { 96 | -webkit-transform: translate3d(0, -10px, 0); 97 | transform: translate3d(0, -10px, 0); } 98 | 90% { 99 | -webkit-transform: translate3d(0, 5px, 0); 100 | transform: translate3d(0, 5px, 0); } 101 | to { 102 | -webkit-transform: translate3d(0, 0, 0); 103 | transform: translate3d(0, 0, 0); } } 104 | 105 | @-webkit-keyframes fadeIn { 106 | from { 107 | opacity: 0; } 108 | to { 109 | opacity: 1; } } 110 | 111 | @keyframes fadeIn { 112 | from { 113 | opacity: 0; } 114 | to { 115 | opacity: 1; } } 116 | 117 | .dxp-transition-in #adminmenuwrap, .dxp-transition-in #adminmenuback { 118 | -webkit-animation-name: bounceInDown; 119 | animation-name: bounceInDown; 120 | -webkit-animation-duration: .7s; 121 | animation-duration: .7s; 122 | -webkit-animation-fill-mode: both; 123 | animation-fill-mode: both; } 124 | 125 | .dxp-transition-out #adminmenuwrap, .dxp-transition-out #adminmenuback { 126 | -webkit-animation-name: bounceInUp; 127 | animation-name: bounceInUp; 128 | -webkit-animation-duration: .7s; 129 | animation-duration: .7s; 130 | -webkit-animation-fill-mode: both; 131 | animation-fill-mode: both; } 132 | 133 | body:not(.dxp-dashboard) #wp-admin-bar-switch-to-dxp-backburger a > div:before { 134 | background: url("../assets/images/backburger-inverse.png") no-repeat center center; 135 | content: ''; 136 | display: inline-block; 137 | height: 32px; 138 | width: 20px; } 139 | 140 | body:not(.dxp-dashboard) #wpadminbar #wp-admin-bar-switch-to-dxp a, 141 | body:not(.dxp-dashboard) #wpadminbar:not(.mobile) .ab-top-menu > #wp-admin-bar-switch-to-dxp > .ab-item:focus, 142 | body:not(.dxp-dashboard) #wpadminbar.nojq .quicklinks .ab-top-menu > #wp-admin-bar-switch-to-dxp > .ab-item:focus, 143 | body:not(.dxp-dashboard) #wpadminbar:not(.mobile) .ab-top-menu > #wp-admin-bar-switch-to-dxp:hover > .ab-item, 144 | body:not(.dxp-dashboard) #wpadminbar .ab-top-menu > #wp-admin-bar-switch-to-dxp.hover > .ab-item { 145 | background: transparent url(../assets/images/OSDXP-logo-inverse-100.png) 10px 50% no-repeat; 146 | background-size: 80px auto; 147 | color: transparent; 148 | display: block; 149 | font: 0/0 a; 150 | text-shadow: none; 151 | width: 80px; } 152 | 153 | body:not(.dxp-dashboard) #wp-toolbar > ul > #wp-admin-bar-switch-to-dxp { 154 | display: block; } 155 | 156 | /*# sourceMappingURL=style-wp-admin.css.map */ 157 | -------------------------------------------------------------------------------- /build/style-wp-admin.min.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes bounceInUp{60%,75%,90%,from,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}from{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{60%,75%,90%,from,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}from{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@-webkit-keyframes bounceInDown{60%,75%,90%,from,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInDown{60%,75%,90%,from,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@-webkit-keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes fadeIn{from{opacity:0}to{opacity:1}}.dxp-transition-in #adminmenuback,.dxp-transition-in #adminmenuwrap{-webkit-animation-name:bounceInDown;animation-name:bounceInDown;-webkit-animation-duration:.7s;animation-duration:.7s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.dxp-transition-out #adminmenuback,.dxp-transition-out #adminmenuwrap{-webkit-animation-name:bounceInUp;animation-name:bounceInUp;-webkit-animation-duration:.7s;animation-duration:.7s;-webkit-animation-fill-mode:both;animation-fill-mode:both}body:not(.dxp-dashboard) #wp-admin-bar-switch-to-dxp-backburger a>div:before{background:url(../assets/images/backburger-inverse.png) no-repeat center center;content:'';display:inline-block;height:32px;width:20px}body:not(.dxp-dashboard) #wpadminbar #wp-admin-bar-switch-to-dxp a,body:not(.dxp-dashboard) #wpadminbar .ab-top-menu>#wp-admin-bar-switch-to-dxp.hover>.ab-item,body:not(.dxp-dashboard) #wpadminbar.nojq .quicklinks .ab-top-menu>#wp-admin-bar-switch-to-dxp>.ab-item:focus,body:not(.dxp-dashboard) #wpadminbar:not(.mobile) .ab-top-menu>#wp-admin-bar-switch-to-dxp:hover>.ab-item,body:not(.dxp-dashboard) #wpadminbar:not(.mobile) .ab-top-menu>#wp-admin-bar-switch-to-dxp>.ab-item:focus{background:transparent url(../assets/images/OSDXP-logo-inverse-100.png) 10px 50% no-repeat;background-size:80px auto;color:transparent;display:block;font:0/0 a;text-shadow:none;width:80px}body:not(.dxp-dashboard) #wp-toolbar>ul>#wp-admin-bar-switch-to-dxp{display:block} -------------------------------------------------------------------------------- /build/wp-admin-bar.css: -------------------------------------------------------------------------------- 1 | body:not(.dxp-dashboard) #wp-admin-bar-switch-to-dxp-backburger a > div:before { 2 | background: url("../assets/images/backburger-inverse.png") no-repeat center center; 3 | content: ''; 4 | display: inline-block; 5 | height: 32px; 6 | width: 20px; } 7 | 8 | body:not(.dxp-dashboard) #wpadminbar #wp-admin-bar-switch-to-dxp a, 9 | body:not(.dxp-dashboard) #wpadminbar:not(.mobile) .ab-top-menu > #wp-admin-bar-switch-to-dxp > .ab-item:focus, 10 | body:not(.dxp-dashboard) #wpadminbar.nojq .quicklinks .ab-top-menu > #wp-admin-bar-switch-to-dxp > .ab-item:focus, 11 | body:not(.dxp-dashboard) #wpadminbar:not(.mobile) .ab-top-menu > #wp-admin-bar-switch-to-dxp:hover > .ab-item, 12 | body:not(.dxp-dashboard) #wpadminbar .ab-top-menu > #wp-admin-bar-switch-to-dxp.hover > .ab-item { 13 | background: transparent url(../assets/images/OSDXP-logo-inverse-100.png) 10px 50% no-repeat; 14 | background-size: 80px auto; 15 | color: transparent; 16 | display: block; 17 | font: 0/0 a; 18 | text-shadow: none; 19 | width: 80px; } 20 | 21 | body:not(.dxp-dashboard) #wp-toolbar > ul > #wp-admin-bar-switch-to-dxp { 22 | display: block; } 23 | 24 | /*# sourceMappingURL=wp-admin-bar.css.map */ 25 | -------------------------------------------------------------------------------- /build/wp-admin-bar.min.css: -------------------------------------------------------------------------------- 1 | body:not(.dxp-dashboard) #wp-admin-bar-switch-to-dxp-backburger a>div:before{background:url(../assets/images/backburger-inverse.png) no-repeat center center;content:'';display:inline-block;height:32px;width:20px}body:not(.dxp-dashboard) #wpadminbar #wp-admin-bar-switch-to-dxp a,body:not(.dxp-dashboard) #wpadminbar .ab-top-menu>#wp-admin-bar-switch-to-dxp.hover>.ab-item,body:not(.dxp-dashboard) #wpadminbar.nojq .quicklinks .ab-top-menu>#wp-admin-bar-switch-to-dxp>.ab-item:focus,body:not(.dxp-dashboard) #wpadminbar:not(.mobile) .ab-top-menu>#wp-admin-bar-switch-to-dxp:hover>.ab-item,body:not(.dxp-dashboard) #wpadminbar:not(.mobile) .ab-top-menu>#wp-admin-bar-switch-to-dxp>.ab-item:focus{background:transparent url(../assets/images/OSDXP-logo-inverse-100.png) 10px 50% no-repeat;background-size:80px auto;color:transparent;display:block;font:0/0 a;text-shadow:none;width:80px}body:not(.dxp-dashboard) #wp-toolbar>ul>#wp-admin-bar-switch-to-dxp{display:block} -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "osdxp/osdxp-dashboard", 3 | "type": "wordpress-plugin", 4 | "description": "An Augmentation of WordPress, creating a task-oriented Digital Experience Platform environment.", 5 | "keywords": ["wordpress", "plugin", "dxp", "osdxp", "dashboard", "enterprise", "productivity"], 6 | "license": "GPL-2.0-only", 7 | "support": { 8 | "email": "development@osdxp.org", 9 | "issues": "https://github.com/osDXP/osdxp-dashboard/issues", 10 | "docs": "https://osdxp.github.io/docs", 11 | "chat": "https://osdxp.slack.com" 12 | }, 13 | "authors": [ 14 | { 15 | "name": "osDXP", 16 | "email": "development@osdxp.org", 17 | "homepage": "https://osdxp.org/" 18 | } 19 | ], 20 | "require": { 21 | "yahnis-elsts/plugin-update-checker": "^4.8.0", 22 | "composer/installers": "^1.0", 23 | "php": ">=7.2.0" 24 | }, 25 | "suggest": { 26 | "squizlabs/php_codesniffer": "Use phpcs with the included ruleset to lint your php files." 27 | }, 28 | "autoload": { 29 | "classmap": ["includes/"] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "d98726883be0aa82e138776ffd23b57b", 8 | "packages": [ 9 | { 10 | "name": "composer/installers", 11 | "version": "v1.9.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/composer/installers.git", 15 | "reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/composer/installers/zipball/b93bcf0fa1fccb0b7d176b0967d969691cd74cca", 20 | "reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "composer-plugin-api": "^1.0 || ^2.0" 25 | }, 26 | "replace": { 27 | "roundcube/plugin-installer": "*", 28 | "shama/baton": "*" 29 | }, 30 | "require-dev": { 31 | "composer/composer": "1.6.* || 2.0.*@dev", 32 | "composer/semver": "1.0.* || 2.0.*@dev", 33 | "phpunit/phpunit": "^4.8.36", 34 | "sebastian/comparator": "^1.2.4", 35 | "symfony/process": "^2.3" 36 | }, 37 | "type": "composer-plugin", 38 | "extra": { 39 | "class": "Composer\\Installers\\Plugin", 40 | "branch-alias": { 41 | "dev-master": "1.0-dev" 42 | } 43 | }, 44 | "autoload": { 45 | "psr-4": { 46 | "Composer\\Installers\\": "src/Composer/Installers" 47 | } 48 | }, 49 | "notification-url": "https://packagist.org/downloads/", 50 | "license": [ 51 | "MIT" 52 | ], 53 | "authors": [ 54 | { 55 | "name": "Kyle Robinson Young", 56 | "email": "kyle@dontkry.com", 57 | "homepage": "https://github.com/shama" 58 | } 59 | ], 60 | "description": "A multi-framework Composer library installer", 61 | "homepage": "https://composer.github.io/installers/", 62 | "keywords": [ 63 | "Craft", 64 | "Dolibarr", 65 | "Eliasis", 66 | "Hurad", 67 | "ImageCMS", 68 | "Kanboard", 69 | "Lan Management System", 70 | "MODX Evo", 71 | "MantisBT", 72 | "Mautic", 73 | "Maya", 74 | "OXID", 75 | "Plentymarkets", 76 | "Porto", 77 | "RadPHP", 78 | "SMF", 79 | "Thelia", 80 | "Whmcs", 81 | "WolfCMS", 82 | "agl", 83 | "aimeos", 84 | "annotatecms", 85 | "attogram", 86 | "bitrix", 87 | "cakephp", 88 | "chef", 89 | "cockpit", 90 | "codeigniter", 91 | "concrete5", 92 | "croogo", 93 | "dokuwiki", 94 | "drupal", 95 | "eZ Platform", 96 | "elgg", 97 | "expressionengine", 98 | "fuelphp", 99 | "grav", 100 | "installer", 101 | "itop", 102 | "joomla", 103 | "known", 104 | "kohana", 105 | "laravel", 106 | "lavalite", 107 | "lithium", 108 | "magento", 109 | "majima", 110 | "mako", 111 | "mediawiki", 112 | "modulework", 113 | "modx", 114 | "moodle", 115 | "osclass", 116 | "phpbb", 117 | "piwik", 118 | "ppi", 119 | "puppet", 120 | "pxcms", 121 | "reindex", 122 | "roundcube", 123 | "shopware", 124 | "silverstripe", 125 | "sydes", 126 | "sylius", 127 | "symfony", 128 | "typo3", 129 | "wordpress", 130 | "yawik", 131 | "zend", 132 | "zikula" 133 | ], 134 | "support": { 135 | "issues": "https://github.com/composer/installers/issues", 136 | "source": "https://github.com/composer/installers/tree/v1.9.0" 137 | }, 138 | "funding": [ 139 | { 140 | "url": "https://packagist.com", 141 | "type": "custom" 142 | }, 143 | { 144 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 145 | "type": "tidelift" 146 | } 147 | ], 148 | "time": "2020-04-07T06:57:05+00:00" 149 | }, 150 | { 151 | "name": "yahnis-elsts/plugin-update-checker", 152 | "version": "v4.10", 153 | "source": { 154 | "type": "git", 155 | "url": "https://github.com/YahnisElsts/plugin-update-checker.git", 156 | "reference": "0edd15f67822464af8a23285ce367b30d515a855" 157 | }, 158 | "dist": { 159 | "type": "zip", 160 | "url": "https://api.github.com/repos/YahnisElsts/plugin-update-checker/zipball/0edd15f67822464af8a23285ce367b30d515a855", 161 | "reference": "0edd15f67822464af8a23285ce367b30d515a855", 162 | "shasum": "" 163 | }, 164 | "require": { 165 | "ext-json": "*", 166 | "php": ">=5.2.0" 167 | }, 168 | "type": "library", 169 | "autoload": { 170 | "files": [ 171 | "load-v4p10.php" 172 | ] 173 | }, 174 | "notification-url": "https://packagist.org/downloads/", 175 | "license": [ 176 | "MIT" 177 | ], 178 | "authors": [ 179 | { 180 | "name": "Yahnis Elsts", 181 | "email": "whiteshadow@w-shadow.com", 182 | "homepage": "http://w-shadow.com/", 183 | "role": "Developer" 184 | } 185 | ], 186 | "description": "A custom update checker for WordPress plugins and themes. Useful if you can't host your plugin in the official WP repository but still want it to support automatic updates.", 187 | "homepage": "https://github.com/YahnisElsts/plugin-update-checker/", 188 | "keywords": [ 189 | "automatic updates", 190 | "plugin updates", 191 | "theme updates", 192 | "wordpress" 193 | ], 194 | "support": { 195 | "issues": "https://github.com/YahnisElsts/plugin-update-checker/issues", 196 | "source": "https://github.com/YahnisElsts/plugin-update-checker/tree/wp55-auto-updates" 197 | }, 198 | "time": "2020-08-21T11:36:45+00:00" 199 | } 200 | ], 201 | "packages-dev": [], 202 | "aliases": [], 203 | "minimum-stability": "stable", 204 | "stability-flags": [], 205 | "prefer-stable": false, 206 | "prefer-lowest": false, 207 | "platform": { 208 | "php": ">=7.2.0" 209 | }, 210 | "platform-dev": [], 211 | "plugin-api-version": "2.0.0" 212 | } 213 | -------------------------------------------------------------------------------- /default-available-modules.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "success", 3 | "data": { 4 | "osdxp-page-builder/fl-builder.php": { 5 | "name": "osDXP Page Builder", 6 | "logo": "https://s14308.pcdn.co/wp-content/uploads/2017/05/beaver-builder-wp-logo.png", 7 | "description": "Free Up Your Time and Unleash Your Creativity! osDXP Page Builder is a complete design system that includes a drag and drop page builder module and a framework theme. Based on the original WordPress Beaver Builder + Beaver Themer applications, but built for Enterprise grade needs and scalability, osDXP Page Builder is fully osDXP compatible.", 8 | "url": "https://www.wpbeaverbuilder.com/osdxp-page-builder-by-beaver-builder/", 9 | "author": "FastLine Media, Makers of Beaver Builder", 10 | "price": "699.00", 11 | "before-price-text": null, 12 | "after-price-text": "Per Year" 13 | }, 14 | "gravityforms/gravityforms.php": { 15 | "name": "Gravity Forms Enterprise for osDXP", 16 | "logo": "https://gravityforms.s3.us-east-1.amazonaws.com/logos/icon-osDXP.png", 17 | "description": "Quickly build and design your osDXP forms using the intuitive visual form editor. Select your fields, configure your options, and easily embed forms on your osDXP powered site using the built in tools. With the added complexity that comes from moving from a Content Management System to the new features being integrated into DXP's today, osDXP Form Builder is completely compatible with the DXP standards, and our team is on the forefront of creating the project's roadmap to start integration to a user based workflow on the creation and management of DXP sites.", 18 | "url": "https://www.gravityforms.com/gravity-forms-for-osdxp/", 19 | "author": "Rocketgenius", 20 | "price": "559.00", 21 | "before-price-text": "Enterprise Edition", 22 | "after-price-text": "Per Year" 23 | }, 24 | "multilingualpress/multilingualpress.php": { 25 | "name": "osDXP Multilingual", 26 | "logo": "https://ps.w.org/multilingual-press/assets/icon-256x256.png", 27 | "description": "The perfect Multilingual Solution for the Open Source Digital Experience Platform. Built a powerful network of sites, translate them in any language and reach a greater audience - with osDXP Multilingual, the first and most trusted multilingual integration in the osDXP world. Benefit from the expertise of one of the founding partners of osDXP and make your website multilingual. The fully integrated osDXP Multilingual module enables you to publish your website in every language you want. Just add it to your osDXP and create a network of sites in different languages, all interconnected just like you want them to be.", 28 | "url": "https://multilingualpress.org/osdxp-multilingual/", 29 | "author": "Inpsyde GmbH", 30 | "price": "399.00", 31 | "before-price-text": "From", 32 | "after-price-text": "Per Year" 33 | }, 34 | "cf-progressive-profiling/cf-progressive-profiling.php": { 35 | "name": "Progressive Profiling for osDXP", 36 | "logo": "https://osdxp.org/wp-content/uploads/2019/10/progressive-profiling.png", 37 | "description": "Progressive Profiling for osDXP allows you to have dynamic forms for your users and avoid question overload for your site visitors while slowly building trust with them. Collecting information from site visitors can be tricky, if the form is long and complicated many visitors will opt-out. Progressive Profiling allows you to select the information you wish to collect from the visitors and gradually collects the information for each site visitor as they return to your site. (requires Gravity Forms)", 38 | "url": "https://crowdfavorite.com/products/progressive-profiling-for-osdxp/", 39 | "author": "Crowd Favorite", 40 | "price": "199.00", 41 | "before-price-text": null, 42 | "after-price-text": "Per Year" 43 | }, 44 | "cf-dxpct/cf-dxpct.php": { 45 | "name": "DXP Lead Conversion Tool - *Public Beta*", 46 | "logo": "https://osdxp.org/wp-content/uploads/2019/10/icon-large.png", 47 | "description": "DXP Lead Conversion Tool is PUBLIC BETA of a collaboration Between Crowd Favorite and Brainstorm Force, that is experimenting with the successful concepts that were introduced in BSF's very popular Convert Pro Plugin for WordPress, and will extend to create the foundation of an advanced lead generation module that uses a drag and drop editor to help build effective opt-in popups designed to increase interactions and website conversions. Email lists, content promotion, offers and more are easily achieved.", 48 | "url": "#", 49 | "author": "Crowd Favorite", 50 | "price": null, 51 | "before-price-text": null, 52 | "after-price-text": "Coming Soon" 53 | }, 54 | "osdxp-shared-counts/osdxp-shared-counts.php": { 55 | "name": "Shared Counts (osDXP Adaptation)", 56 | "logo": "https://sharedcountsplugin.com/wp-content/uploads/2019/03/shared-counts-icon.png", 57 | "description": "Social sharing buttons that look great and keep your site loading fast. https://sharedcountsplugin.com/", 58 | "url": "https://github.com/osDXP/osdxp-shared-counts", 59 | "author": "Bill Erickson & Jared Atchison - osDXP version by CrowdFavorite", 60 | "price": null, 61 | "before-price-text": null, 62 | "after-price-text": null 63 | }, 64 | "osdxp-login-logo/osdxp-login-logo.php": { 65 | "name": "Login Logo (osDXP Adaptation)", 66 | "logo": "https://s.w.org/plugins/geopattern-icon/login-logo_e7e7e7.svg", 67 | "description": "This plugin has been refactored to work with osDXP. Drop a PNG file named login-logo.png into your wp-content directory. This simple plugin takes care of the rest, with zero configuration. Transparent backgrounds work best. Crop it tight, with a width of 312 pixels, for best results. Original Code Link: http://txfx.net/wordpress-plugins/login-logo/ Donate link: http://txfx.net/wordpress-plugins/donate", 68 | "url": "https://github.com/osDXP/osdxp-login-logo", 69 | "author": "Mark Jaquith - osDXP version by Crowd Favorite", 70 | "price": null, 71 | "before-price-text": null, 72 | "after-price-text": null 73 | }, 74 | "conditional-content-pro-osdxp/conditional-content-pro-osdxp.php": { 75 | "name": "Conditional Content for osDXP", 76 | "logo": "https://osdxp.org/wp-content/uploads/2019/10/conditional-content.png", 77 | "description": "Create custom content as unique as your site visitors. Conditional Content is a Personalization plugin that gives you the ability to add or replace content based on the site visitors' behavior. Conditional Content is the ultimate customization tool, providing you with the ability to change content for the benefit of your visitors, and making it easier for the visitors to find what they are searching for.", 78 | "url": "https://crowdfavorite.com/products/conditional-content-for-osdxp/", 79 | "author": "Crowd Favorite", 80 | "price": "899.00", 81 | "before-price-text": null, 82 | "after-price-text": "Per Year" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const gulp = require('gulp'); 3 | const postcss = require('gulp-postcss'); 4 | const autoprefixer = require('autoprefixer'); 5 | const babel = require('gulp-babel'); 6 | const concat = require('gulp-concat'); 7 | const gulpif = require('gulp-if'); 8 | const rename = require('gulp-rename'); 9 | const sourcemaps = require('gulp-sourcemaps'); 10 | const uglify = require('gulp-uglify'); 11 | const cleanCSS = require('gulp-clean-css'); 12 | const log = require('gulplog'); 13 | const mode = require('gulp-mode')({ 14 | modes: ["production", "development"], 15 | default: "development", 16 | verbose: false 17 | }); 18 | let sass = require('gulp-sass'); 19 | sass.compiler = require('node-sass'); 20 | 21 | // Project paths. 22 | const paths = { 23 | styles: { 24 | src: [ 25 | 'sass/style-admin.scss', 26 | 'sass/style-wp-admin.scss', 27 | 'sass/admin-bar.scss', 28 | 'sass/wp-admin-bar.scss' 29 | ], 30 | dest: '../build', 31 | sass: 'sass/**/*.scss' 32 | }, 33 | scripts: { 34 | src: ['js/**/*.js'], 35 | concat: 'app.js', 36 | dest: '../build', 37 | watch: ['js/**/*.js'] 38 | }, 39 | folders: { 40 | 'assets': 'assets/' 41 | }, 42 | }; 43 | 44 | const isProduction = mode.production(); 45 | 46 | 47 | /** 48 | * Return the styles pipes for an asset from the list. 49 | * 50 | * @param {string} slug The asset slug. 51 | * @param {array} assetList The folders list. 52 | * @param {boolean} forceProduction If the scripts must be forced to be build as production. 53 | * @return {stream} The gulp stream. 54 | */ 55 | function makeStylePipes(slug, assetList, forceProduction) { 56 | const sources = []; 57 | paths.styles.src.forEach((src) => { 58 | const filePath = assetList[slug] + src; 59 | 60 | if (fs.existsSync(filePath)) { 61 | sources.push(filePath); 62 | } 63 | }); 64 | 65 | if (sources.length === 0) return; 66 | 67 | log.info(`Building ${slug} Styles...`); 68 | 69 | return gulp.src(sources) 70 | // Init the sourcemaps. 71 | .pipe(gulpif(!isProduction, sourcemaps.init())) 72 | // Compile the sass. 73 | .pipe(sass().on('error', sass.logError)) 74 | // Add prefixes. 75 | .pipe(postcss([ 76 | autoprefixer() 77 | ])) 78 | // If not in debug mode minify the styles. 79 | .pipe(gulpif(isProduction || forceProduction, cleanCSS())) 80 | .pipe(gulpif(isProduction || forceProduction, rename({suffix: '.min'}))) 81 | // Stop listening and write the sourcemaps. 82 | .pipe(gulpif(!isProduction, sourcemaps.write('.'))) 83 | // Spit it out in the dest folder. 84 | .pipe(gulp.dest(assetList[slug] + paths.styles.dest)).on('end', () => { 85 | log.info(`Finished '${slug}' Styles Build.`); 86 | }); 87 | } 88 | 89 | 90 | /** 91 | * Return the scripts pipes for an asset from the list. 92 | * 93 | * @param {string} slug The asset slug. 94 | * @param {array} assetList The folders list. 95 | * @param {boolean} forceProduction If the scripts must be forced to be build as production. 96 | * @return {stream} The gulp stream. 97 | */ 98 | function makeScriptsPipes(slug, assetList, forceProduction) { 99 | const sources = []; 100 | paths.scripts.src.forEach((src) => { 101 | const filePath = assetList[slug] + src; 102 | 103 | if (fs.existsSync(filePath) || filePath.indexOf('*') !== -1) { 104 | sources.push(filePath); 105 | } 106 | }); 107 | 108 | if (sources.length === 0) return; 109 | 110 | log.info(`Building ${slug} Scripts...`); 111 | 112 | let babelOptions = { 113 | presets: ['airbnb'] 114 | } 115 | 116 | // If production remove console.log, console.error. 117 | if (isProduction) { 118 | babelOptions.plugins = ['transform-remove-console']; 119 | } 120 | 121 | return gulp.src(sources) 122 | .pipe(gulpif(!isProduction, sourcemaps.init())) 123 | .pipe(concat('app.js')) 124 | .pipe(babel(babelOptions)) 125 | .pipe(gulpif(isProduction || forceProduction, uglify())) 126 | .pipe(gulpif(isProduction || forceProduction, rename({suffix: '.min'}))) 127 | .pipe(gulpif(!isProduction, sourcemaps.write('.'))) 128 | // Spit it out in the dest folder. 129 | .pipe(gulp.dest(assetList[slug] + paths.scripts.dest)).on('end', () => { 130 | log.info(`Finished '${slug}' Scripts Build.`); 131 | }); 132 | } 133 | 134 | /** 135 | * Watch everything. 136 | * 137 | * @param {boolean} forceProduction If all the watched files must be forced to be build as production. 138 | */ 139 | function watch(forceProduction) { 140 | // Watch every folder STYLES. 141 | Object.keys(paths.folders).forEach((folder) => { 142 | // Watch all the folder sass files. 143 | gulp.watch(paths.folders[folder] + paths.styles.sass, function watchStyle() { 144 | if (forceProduction) production(); 145 | return makeStylePipes(folder, paths.folders); 146 | }); 147 | }); 148 | 149 | // Watch every folder SCRIPTS. 150 | Object.keys(paths.folders).forEach((folder) => { 151 | // Watch the folder js files. 152 | gulp.watch(paths.folders[folder] + paths.scripts.watch, function watchScript() { 153 | if (forceProduction) production(); 154 | return makeScriptsPipes(folder, paths.folders); 155 | }); 156 | }); 157 | } 158 | 159 | /** 160 | * Build everything. 161 | */ 162 | function build() { 163 | // Build every folder STYLES. 164 | Object.keys(paths.folders).forEach((folder) => { 165 | return makeStylePipes(folder, paths.folders); 166 | }); 167 | 168 | // Build every folder SCRIPTS. 169 | Object.keys(paths.folders).forEach((folder) => { 170 | return makeScriptsPipes(folder, paths.folders); 171 | }); 172 | } 173 | 174 | /** 175 | * Production everything. 176 | */ 177 | function production() { 178 | // Build for production every folder STYLES. 179 | Object.keys(paths.folders).forEach((folder) => { 180 | return makeStylePipes(folder, paths.folders, true); 181 | }); 182 | 183 | // Build for production every folder SCRIPTS. 184 | Object.keys(paths.folders).forEach((folder) => { 185 | return makeScriptsPipes(folder, paths.folders, true); 186 | }); 187 | } 188 | 189 | gulp.task('watch', function() { 190 | watch(); 191 | }); 192 | gulp.task('watch-prod', function() { 193 | watch(true); 194 | }); 195 | gulp.task('build', async function() { 196 | build(); 197 | }); 198 | -------------------------------------------------------------------------------- /includes/assets.php: -------------------------------------------------------------------------------- 1 | wp_create_nonce('wp_rest'), 90 | 'restUrl' => get_rest_url(null, OSDXP_DASHBOARD_REST_NAMESPACE), 91 | 'text' => [ 92 | 'licenseKeyRemovalConfirmation' => esc_html__( 93 | 'Are you sure you want to remove this license key?', 94 | 'osdxp-dashboard' 95 | ), 96 | ], 97 | ] 98 | ); 99 | 100 | wp_enqueue_script(OSDXP_DASHBOARD_HANDLE); 101 | } else { 102 | wp_enqueue_style(OSDXP_DASHBOARD_HANDLE . 'wp-admin'); 103 | } 104 | } 105 | 106 | /** 107 | * Method to load frontend assets. 108 | * 109 | * @return void 110 | */ 111 | function load_frontend_assets() 112 | { 113 | $env = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '.' : '.min.'; 114 | 115 | // Register the plugin admin bar custom style. 116 | wp_register_style( 117 | OSDXP_DASHBOARD_HANDLE . '-admin-bar', 118 | OSDXP_DASHBOARD_URL . 'build/admin-bar' . $env . 'css', 119 | [ 'admin-bar' ], 120 | filemtime(OSDXP_DASHBOARD_DIR . 'build/admin-bar' . $env . 'css'), 121 | false 122 | ); 123 | 124 | // Register the plugin wp admin bar custom style. 125 | wp_register_style( 126 | OSDXP_DASHBOARD_HANDLE . '-wp-admin-bar', 127 | OSDXP_DASHBOARD_URL . 'build/wp-admin-bar' . $env . 'css', 128 | [ 'admin-bar' ], 129 | filemtime(OSDXP_DASHBOARD_DIR . 'build/wp-admin-bar' . $env . 'css') 130 | ); 131 | 132 | if (is_dxp_dashboard_active_for_current_user()) { 133 | // Enqueue the plugin admin bar custom style. 134 | wp_enqueue_style(OSDXP_DASHBOARD_HANDLE . '-admin-bar'); 135 | } else { 136 | // Enqueue the plugin wp admin bar custom style. 137 | wp_enqueue_style(OSDXP_DASHBOARD_HANDLE . '-wp-admin-bar'); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /includes/class-licenseapi.php: -------------------------------------------------------------------------------- 1 | plugin_slug = $plugin_slug; 70 | } 71 | 72 | /** 73 | * Prevent the object from being deserialized. 74 | * 75 | * @access private 76 | * 77 | * @return void 78 | */ 79 | private function __wakeup() 80 | { 81 | } 82 | 83 | /** 84 | * Build an API request URI. 85 | * 86 | * @access protected 87 | * 88 | * @param string $path Optional. The API endpoint. Default is '/'. 89 | * @param array $args Optional. Query string arguments for the URI. Default is empty. 90 | * 91 | * @return string The URI for the API request. 92 | */ 93 | protected function buildURI($path = '/', array $args = []) 94 | { 95 | // Ensure the $path has a leading slash. 96 | if ('/' !== substr($path, 0, 1)) { 97 | $path = '/' . $path; 98 | } 99 | 100 | return add_query_arg($args, OSDXP_DASHBOARD_API_URL . $path); 101 | } 102 | 103 | /** 104 | * Clear the current value for $this->api_key. 105 | * 106 | * @access public 107 | * 108 | * @return void 109 | */ 110 | public function clearAPIKey() 111 | { 112 | $this->api_key = null; 113 | } 114 | 115 | /** 116 | * Given a URI and arguments, generate a cache key for use with CF's internal caching system. 117 | * 118 | * @param string $uri The API URI, with any query string arguments. 119 | * @param array $args Optional. An array of HTTP arguments used in the request. Default is empty. 120 | * 121 | * @return string A cache key. 122 | */ 123 | public static function generateCacheKey($uri, $args = []) 124 | { 125 | return 'osdxp_dashboard_' . substr(md5($uri . wp_json_encode($args)), 0, 12); 126 | } 127 | 128 | /** 129 | * Retrieve an *uncached* response from the /account endpoint. 130 | * 131 | * @access public 132 | * 133 | * @return array|WP_Error An array of all account attributes or a WP_Error object on error. 134 | */ 135 | public function getAccount() 136 | { 137 | $response = $this->sendRequest('GET', '/account'); 138 | 139 | return $response; 140 | } 141 | 142 | /** 143 | * Retrieve the API key. 144 | * 145 | * @access public 146 | * 147 | * @return string The API key. 148 | */ 149 | public function getAPIKey() 150 | { 151 | if ($this->api_key) { 152 | return $this->api_key; 153 | } 154 | 155 | $this->api_key = get_license_key($this->plugin_slug); 156 | 157 | return $this->api_key; 158 | } 159 | 160 | /** 161 | * Retrieve any API errors that have occurred. 162 | * 163 | * @access public 164 | * 165 | * @return array An array of WP_Error objects. 166 | */ 167 | public function getErrors() 168 | { 169 | return $this->errors; 170 | } 171 | 172 | /** 173 | * Method to initialize the update checker. 174 | * 175 | * @access public 176 | * 177 | * @return void 178 | */ 179 | public function initUpdateChecker() 180 | { 181 | /** 182 | * Filters the plugin file. 183 | * 184 | * @param null|string $plugin_slug Plugin slug. 185 | */ 186 | $plugin_file = apply_filters('osdxp_dashboard_plugin_file_' . $this->plugin_slug, null); 187 | 188 | /** 189 | * Filters the plugin update metadata URL. 190 | * 191 | * @param null|string $plugin_slug Plugin slug. 192 | */ 193 | $metadata_url = apply_filters( 194 | 'osdxp_dashboard_plugin_update_metadata_url_' . $this->plugin_slug, 195 | OSDXP_DASHBOARD_UPDATE_API_URL 196 | ); 197 | 198 | // Skip this plugin if no plugin file was specified or if no license key was found. 199 | if (!$plugin_file || null === $this->getAPIKey()) { 200 | return; 201 | } 202 | // Initialize checker. 203 | $updater = new OsdxpModuleUpdateChecker( 204 | $metadata_url, 205 | $plugin_file, 206 | $this->plugin_slug 207 | ); 208 | 209 | $updater->addHttpRequestArgFilter([$this, 'updateCheckerHttpRequestArgFilter']); 210 | 211 | $updater->addResultFilter([$this, 'updateCheckerResultFilter']); 212 | } 213 | 214 | /** 215 | * Send a request to the license server. 216 | * 217 | * @access protected 218 | * 219 | * @param string $method The HTTP method. 220 | * @param string $path The API request path. 221 | * @param array $query Optional. Query string arguments. Default is empty. 222 | * @param array $args Optional. Additional HTTP arguments. For a full list of options, 223 | * see wp_remote_request(). 224 | * @param int $cache Optional. The number of seconds for which the result should be cached. 225 | * Default is 0 seconds (no caching). 226 | * 227 | * @return array|WP_Error The HTTP response body or a WP_Error object if something went wrong. 228 | */ 229 | protected function sendRequest($method, $path, $query = [], $args = [], $cache = 0) 230 | { 231 | $api_key = $this->getAPIKey(); 232 | 233 | if (empty($api_key)) { 234 | return new WP_Error( 235 | 'osdxp-dashboard-no-api-key', 236 | esc_html__('No API key has been set, unable to make request.', 'osdxp-dashboard') 237 | ); 238 | } 239 | 240 | $uri = $this->buildURI($path, $query); 241 | $args = wp_parse_args( 242 | $args, 243 | [ 244 | 'timeout' => 30, 245 | 'user-agent' => self::USER_AGENT, 246 | 'headers' => [ 247 | 'Authorization' => 'Bearer ' . $api_key, 248 | 'Method' => $method, 249 | 'Slug' => $this->plugin_slug, 250 | 'X-Forwarded-Host' => site_url(), 251 | ], 252 | ] 253 | ); 254 | 255 | $cache_key = self::generateCacheKey($uri, $args); 256 | $cached = get_transient($cache_key); 257 | 258 | // Return the cached version, if we have it. 259 | if ($cache && $cached) { 260 | return $cached; 261 | } 262 | 263 | $response = wp_remote_request($uri, $args); 264 | 265 | if (is_wp_error($response)) { 266 | return $response; 267 | } 268 | 269 | $body = json_decode(wp_remote_retrieve_body($response), true); 270 | 271 | if ('fail' === $body['status']) { 272 | return new WP_Error( 273 | 'osdxp-dashboard-api', 274 | /* Translators: %1$s is the first error message from the API response. */ 275 | sprintf( 276 | esc_html__('The license API request failed: %1$s', 'osdxp-dashboard'), 277 | current((array)$body['data']) 278 | ), 279 | $body['data'] 280 | ); 281 | } 282 | 283 | // Cache the result. 284 | if ($cache) { 285 | set_transient($cache_key, $body['data'], $cache); 286 | } 287 | 288 | return $body['data']; 289 | } 290 | 291 | /** 292 | * Explicitly set the API key. 293 | * 294 | * @access public 295 | * 296 | * @param string $key The API key to use. 297 | * 298 | * @return void 299 | */ 300 | public function setAPIKey($key) 301 | { 302 | $this->api_key = $key; 303 | } 304 | 305 | /** 306 | * Callback method for the \OSDXP_Dashboard\OsdxpModuleUpdateChecker::addHttpRequestArgFilter() method. 307 | * 308 | * @access public 309 | * 310 | * @param array $options Options array. 311 | * 312 | * @return array 313 | */ 314 | public function updateCheckerHttpRequestArgFilter($options) 315 | { 316 | $api_key = $this->getAPIKey(); 317 | 318 | if (!is_array($options)) { 319 | $options = []; 320 | } 321 | 322 | if (empty($options['headers']) || ! is_array($options['headers'])) { 323 | $options['headers'] = []; 324 | } 325 | 326 | $options['headers'] = array_merge( 327 | $options['headers'], 328 | [ 329 | 'Authorization' => $api_key, 330 | 'Slug' => $this->plugin_slug, 331 | 'X-Forwarded-Host' => site_url(), 332 | ] 333 | ); 334 | 335 | return $options; 336 | } 337 | 338 | /** 339 | * Callback method for the \OSDXP_Dashboard\OsdxpModuleUpdateChecker::addResultFilter(). 340 | * method. 341 | * 342 | * @access public 343 | * 344 | * @param mixed $plugin_info Plugin info. 345 | * @param array $result Result. 346 | * 347 | * @return mixed 348 | */ 349 | public function updateCheckerResultFilter($plugin_info, $result) 350 | { 351 | $code = empty($result['response']['code']) ? 0 : (int)$result['response']['code']; 352 | 353 | if (!$code || $code < 200 || $code > 299) { 354 | $body = empty($result['body']) ? null : json_decode($result['body']); 355 | 356 | if (!empty($body->authorization)) { 357 | set_site_transient(get_license_error_transient_name($this->plugin_slug), $body->authorization); 358 | } 359 | } else { 360 | delete_site_transient(get_license_error_transient_name($this->plugin_slug)); 361 | } 362 | 363 | return $plugin_info; 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /includes/class-osdxp-render-available-modules.php: -------------------------------------------------------------------------------- 1 |

%1$s

', $message); 20 | } 21 | 22 | 23 | /** 24 | * Fetch the remote JSON data from the license server 25 | * 26 | * @param string $url URL to the JSON data for the modules 27 | * 28 | * @return string JSON data that contains the avaialble modules 29 | */ 30 | protected function getModulesRemoteModulesData(string $url = OSDXP_DASHBOARD_AVAILABLE_MODULES_LIST_URL) 31 | { 32 | // account for local-only modules 33 | if (false === $url) { 34 | return false; 35 | } 36 | 37 | $response = wp_remote_get($url); 38 | if (is_wp_error($response)) { 39 | add_action('admin_notices', function () use ($response) { 40 | printf( 41 | '

%1$s

', 42 | esc_attr('Failed to retrieve available modules: ' . $response->get_error_message()) 43 | ); 44 | }); 45 | return false; 46 | } 47 | $data = wp_remote_retrieve_body($response); 48 | // check to make sure $response is valid JSON data 49 | $validate = $this->validateModulesJSON($data); 50 | if (!$validate) { 51 | // Not valid JSON 52 | return false; 53 | } 54 | 55 | return $data; 56 | } 57 | 58 | /** 59 | * Set the WP Transient with the available modules JSON 60 | * 61 | * @param string $json JSON data to be saved in the transient 62 | * @param string $transientName name of the transient 63 | * @param int $expireTime number of seconds to expiration 64 | * 65 | * @return boolean Success of the storing of the transient 66 | */ 67 | protected function setModulesDataTransient(string $json) 68 | { 69 | $result = set_transient( 70 | OSDXP_DASHBOARD_AVAILABLE_MODULES_TRANSIENT, 71 | $json, 72 | OSDXP_DASHBOARD_AVAILABLE_MODULES_TRANSIENT_EXPIRE 73 | ); 74 | 75 | return $result; 76 | } 77 | 78 | /** 79 | * Transform the JSON data of available modules 80 | * 81 | * Convert JSON data to a PHP array 82 | * @todo: is this somehting that needs functionaluty for? "Wherever possible, t 83 | * he word WordPress is removed and replaced with DXP 84 | * in the module name" 85 | * 86 | * @access public 87 | * 88 | * @param string $json JSON string to be transformed 89 | * 90 | * @return array $data array that contains the transformed data 91 | */ 92 | public function transformData(string $json) 93 | { 94 | $data = array_change_key_case_recursive( 95 | json_decode( 96 | $json, 97 | true 98 | ) 99 | ); 100 | 101 | $data_names = array_column($data['data'], 'name'); 102 | $partner_data = []; 103 | $partner_data = array_change_key_case_recursive( 104 | apply_filters( 105 | 'osdxp_get_available_modules', 106 | $partner_data 107 | ) 108 | ); 109 | foreach ($partner_data as $key => $partner) { 110 | if (in_array($partner['name'], $data_names)) { 111 | continue; 112 | } 113 | $data['data'][$key] = $partner; 114 | } 115 | 116 | return $data['data']; 117 | } 118 | 119 | /** 120 | * Wrapper method to get the JSON data for available modules. 121 | * 1st attempt to get data from transient 122 | * 2nd attempt to get data from remote 123 | * 3rd and final, get data from a static file. This file should be the most 124 | * up to date of the listing if possible. If should be a rare case that 125 | * the file would ever get used 126 | * 127 | * @return array the JSON data of available modules transformed to an array 128 | */ 129 | public function getModulesData() 130 | { 131 | $jsonData = get_transient(OSDXP_DASHBOARD_AVAILABLE_MODULES_TRANSIENT); 132 | //Transient doesn't exist, let's go fetch the info and reset the transient 133 | if (!$jsonData) { 134 | $jsonData = $this->getModulesRemoteModulesData(); 135 | 136 | // if for some reason, the remote call fails, fallback to default data file. 137 | if (!$jsonData) { 138 | $jsonData = file_get_contents(OSDXP_DASHBOARD_DIR . '/default-available-modules.json'); 139 | } 140 | 141 | // Set this data to the transient after either retreiving remotely or default 142 | $this->setModulesDataTransient($jsonData); 143 | } 144 | 145 | return $jsonData; 146 | } 147 | 148 | /** 149 | * Make sure the the data contained in the JSON matches the expected values 150 | * 151 | * @param string $json [description] 152 | * 153 | * @return boolean true if valid false if not 154 | */ 155 | protected function validateModulesJSON(string $json) 156 | { 157 | $results = json_decode($json, true); 158 | 159 | // If it is not valid JOSN, return false 160 | if (!$results) { 161 | // Not valid JSON 162 | return false; 163 | } 164 | if (!isset($results['data'])) { 165 | // No proper data 166 | return false; 167 | } 168 | $results = $results['data']; 169 | // This needs to match the properties in the JSON file 170 | $expectedModuleKeys = [ 171 | 'logo', 172 | 'name', 173 | 'description', 174 | 'url', 175 | 'author', 176 | 'price', 177 | 'before-price-text', 178 | 'after-price-text' 179 | ]; 180 | $ksort_expected_module_keys = ksort($expectedModuleKeys); 181 | // Check if the proper properties exist in the data. 182 | foreach ($results as $moduleData) { 183 | $ksort_module_data_keys = array_keys($moduleData); 184 | $ksort_module_data_keys = ksort($ksort_module_data_keys); 185 | if (!($ksort_expected_module_keys == $ksort_module_data_keys)) { 186 | return false; 187 | } 188 | } 189 | 190 | return true; 191 | } 192 | 193 | /** 194 | * Outupt 'page' for the available modules. Template is in a separate file 195 | * 196 | * @return void 197 | */ 198 | public function showPage() 199 | { 200 | $jsonData = $this->getModulesData(); 201 | $data = $this->transformData($jsonData); 202 | include OSDXP_DASHBOARD_DIR . '/templates/modules-list-available.php'; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /includes/config.php: -------------------------------------------------------------------------------- 1 | OSDXP_DASHBOARD_MENU_TYPE_MENU, 25 | // Arguments as required by add_menu_page(). 26 | 'page_title' => esc_html__('Modules', 'osdxp-dashboard'), 27 | 'menu_title' => esc_html__('Modules', 'osdxp-dashboard'), 28 | 'capability' => 'manage_options', 29 | 'menu_slug' => 'dxp-modules-installed', 30 | 'function' => __NAMESPACE__ . '\\dxp_render_installed_modules', 31 | 'position_network' => '18', 32 | 'network' => 'both', //omit to hide from network, true for only, both for both 33 | ], 34 | [ 35 | //declare first submenu page from dxp-modules to rewrite title for autogenerated subpage 36 | 'type' => OSDXP_DASHBOARD_MENU_TYPE_SUBMENU, 37 | // Arguments as required by add_submenu_page(). 38 | 'parent_slug' => 'dxp-modules-installed', 39 | 'page_title' => esc_html__('Installed Modules', 'osdxp-dashboard'), 40 | 'menu_title' => esc_html__('Installed Modules', 'osdxp-dashboard'), 41 | 'capability' => 'manage_options', 42 | 'menu_slug' => 'dxp-modules-installed', 43 | 'function' => __NAMESPACE__ . '\\dxp_render_installed_modules', 44 | 'network' => 'both' 45 | ], 46 | [ 47 | 'type' => OSDXP_DASHBOARD_MENU_TYPE_SUBMENU, 48 | 'parent_slug' => 'dxp-modules-installed', 49 | 'page_title' => esc_html__('Available Modules', 'osdxp-dashboard'), 50 | 'menu_title' => esc_html__('Available Modules', 'osdxp-dashboard'), 51 | 'capability' => 'manage_options', 52 | 'menu_slug' => 'dxp-modules', 53 | 'function' => __NAMESPACE__ . '\\dxp_render_available_modules', 54 | 'network' => 'both', 55 | ], 56 | [ 57 | 'type' => OSDXP_DASHBOARD_MENU_TYPE_ENDPOINT, 58 | 'parent_slug' => 'dxp-modules-installed', 59 | 'page_title' => esc_html__('Upgrade Modules', 'osdxp-dashboard'), 60 | 'menu_title' => esc_html__('Upgrade Modules', 'osdxp-dashboard'), 61 | 'capability' => 'manage_options', 62 | 'menu_slug' => 'dxp-modules-update', 63 | 'function' => __NAMESPACE__ . '\\dxp_render_update_modules', 64 | 'network' => 'both' 65 | ], 66 | [ 67 | 'type' => OSDXP_DASHBOARD_MENU_TYPE_ENDPOINT, 68 | 'parent_slug' => 'dxp-modules-installed', 69 | 'page_title' => esc_html__('Modules Information', 'osdxp-dashboard'), 70 | 'menu_title' => esc_html__('Modules Information', 'osdxp-dashboard'), 71 | 'capability' => 'manage_options', 72 | 'menu_slug' => 'dxp-modules-information', 73 | 'function' => __NAMESPACE__ . '\\dxp_render_update_modules_information', 74 | 'network' => 'both' 75 | 76 | ], 77 | [ 78 | 'type' => OSDXP_DASHBOARD_MENU_TYPE_MENU, 79 | 'page_title' => esc_html__('Module Settings', 'osdxp-dashboard'), 80 | 'menu_title' => esc_html__('Module Settings', 'osdxp-dashboard'), 81 | 'capability' => 'manage_options', 82 | 'menu_slug' => 'dxp-module-settings', 83 | 'function' => __NAMESPACE__ . '\\dxp_render_module_settings', 84 | 'position_network' => '19', 85 | 'network' => 'both' 86 | ], 87 | ]; 88 | 89 | $custom_pages = []; 90 | $custom_pages = apply_filters('osdxp_add_module_settings_page', $custom_pages); 91 | 92 | foreach ($custom_pages as $custom_page) { 93 | if ( 94 | isset($custom_page['page_title']) 95 | && isset($custom_page['menu_title']) 96 | && isset($custom_page['menu_slug']) 97 | && isset($custom_page['function']) 98 | ) { 99 | $custom_page['type'] = isset($custom_page['type']) 100 | ? $custom_page['type'] 101 | : OSDXP_DASHBOARD_MENU_TYPE_SUBMENU; 102 | $custom_page['parent_slug'] = isset($custom_page['parent_slug']) 103 | ? $custom_page['parent_slug'] 104 | : 'dxp-module-settings'; 105 | $custom_page['capability'] = isset($custom_page['capability']) 106 | ? $custom_page['capability'] 107 | : 'manage_options'; 108 | 109 | $dxp_pages[] = $custom_page; 110 | } 111 | } 112 | } 113 | 114 | return $dxp_pages; 115 | } 116 | 117 | /** 118 | * Get update data for modules 119 | * 120 | * @return int number of module updates 121 | */ 122 | function osdxp_get_update_data() 123 | { 124 | $count = 0; 125 | 126 | if (current_user_can('update_plugins')) { 127 | $update_modules = get_site_transient('update_plugins'); 128 | $all_modules = apply_filters('osdxp_get_modules', get_plugins()); 129 | if (isset($update_modules->response)) { 130 | $response = $update_modules->response; 131 | $count = count(array_intersect_key($response, $all_modules)); 132 | } 133 | } 134 | 135 | return $count; 136 | } 137 | -------------------------------------------------------------------------------- /includes/core.php: -------------------------------------------------------------------------------- 1 | OSDXP_DASHBOARD_VER, 34 | 'upgrade' => false, 35 | 'url' => OSDXP_DASHBOARD_SITE, 36 | ]; 37 | } 38 | 39 | /** 40 | * Method to set dxp meta to true when user logs in 41 | * Checks if the user has no dxp meta set 42 | * 43 | * 44 | * @return void 45 | */ 46 | function set_dxp_meta_on_login($redirect_to, $request, $user) 47 | { 48 | if ( 49 | ($user instanceof \WP_User && user_can($user, 'edit_posts')) 50 | && ( 51 | ! array_key_exists('dxp-dashboard', get_metadata('user', $user->ID)) 52 | || get_user_meta($user->ID, 'dxp-dashboard', true) === '1' 53 | ) 54 | ) { 55 | $redirect_to = esc_url(self_admin_url('?dxp=on')); 56 | } 57 | return $redirect_to; 58 | } 59 | 60 | /** 61 | * Method to check and activate the DXP Dashboard, if the user is accessing a DXP Dashboard page, 62 | * or if the user has switched to the DXP Dashboard. 63 | * 64 | * We are using the 'init' action and not the 'admin_init', because the latter happens to late, 65 | * and we'll want to execute this code as soon as possible, 66 | * for example, before the admin_menu action. 67 | * 68 | * @return void 69 | */ 70 | function init_dxp_dashboard() 71 | { 72 | // Only execute the following code on the admin. 73 | if (! is_admin()) { 74 | return; 75 | } 76 | 77 | $dxp = false; 78 | $dxp_menu_pages = get_dxp_menu_pages(); 79 | 80 | if ($dxp_menu_pages && is_array($dxp_menu_pages)) { 81 | //get the path without parameters 82 | $url = explode('?', $_SERVER['REQUEST_URI'], 2)[0]; 83 | 84 | //get endpoint 85 | $page = explode('/', $url)[2]; 86 | 87 | //check if on multisite 88 | $network = ($page === 'network'); 89 | 90 | //set denied pages 91 | $denied_pages = [ 92 | 'edit-comments.php', 93 | 'plugins.php', 94 | 'plugin-install.php', 95 | 'plugin-editor.php' 96 | ]; 97 | 98 | //check if page is denied 99 | $page = !(in_array($page, $denied_pages)); 100 | 101 | //check if query has dxp param 102 | if (isset($_REQUEST['dxp'])) { // phpcs:ignore 103 | $dxp = ($_REQUEST['dxp'] === 'on') ? true : false; // phpcs:ignore 104 | 105 | //check if on network dashboard 106 | } elseif ($network) { 107 | $dxp = true; 108 | 109 | //check if user opted out of dxp 110 | } elseif (! is_dxp_dashboard_active_for_current_user()) { 111 | $dxp = false; 112 | 113 | //check if accepted pages 114 | } elseif ($page) { 115 | $dxp = true; 116 | 117 | // check if page parameter is in dxp pages 118 | } elseif (isset($_REQUEST['page']) && false !== array_search($_REQUEST['page'], array_column($dxp_menu_pages, 'menu_slug'))) { // phpcs:ignore 119 | $dxp = true; 120 | } 121 | } 122 | 123 | //define the constant to if we're dxp or not 124 | if (apply_filters('osdxp_dashboard_is_active', true) && ! defined('OSDXP_DASHBOARD_IS_ACTIVE')) { 125 | define('OSDXP_DASHBOARD_IS_ACTIVE', $dxp); 126 | } 127 | } 128 | 129 | /** 130 | * Method to override the "Thank you for creating with WordPress." admin footer text with the DXP admin footer text. 131 | * 132 | * @param string $text Admin footer text. 133 | * 134 | * @return string 135 | */ 136 | function override_admin_footer_text($text) 137 | { 138 | if (!is_dxp_dashboard()) { 139 | return $text; 140 | } 141 | 142 | $text = sprintf( 143 | // Translators: %s - Open Source DXP website. 144 | __('Open Source DXP. Powered by WordPress', 'osdxp-dashboard'), 145 | OSDXP_DASHBOARD_SITE, 146 | 'https://wordpress.org' 147 | ); 148 | 149 | return sprintf( 150 | '%s', 151 | $text 152 | ); 153 | } 154 | 155 | /** 156 | * Method to override the admin footer WordPress version text with the DXP version text. 157 | * 158 | * @param string $text Admin footer version text. 159 | * 160 | * @return string 161 | */ 162 | function override_update_footer($text) 163 | { 164 | if (!is_dxp_dashboard()) { 165 | return $text; 166 | } 167 | 168 | // Set the DXP version text. 169 | if (current_user_can('update_plugins')) { 170 | $dxp_version = get_dxp_version_details(); 171 | 172 | if (!empty($dxp_version->upgrade)) { 173 | $dxp_text = sprintf( 174 | // Translators: %1$s: installed DXP version, %2$s: DXP URL, %3$s: latest DXP version. 175 | esc_html__( 176 | 'Open Source DXP %1$s installed. Get current version, %3$s.', 177 | 'osdxp-dashboard' 178 | ), 179 | OSDXP_DASHBOARD_VER, 180 | esc_url($dxp_version->url), 181 | $dxp_version->current 182 | ); 183 | } 184 | } 185 | 186 | // DXP version text fallback. 187 | if (empty($dxp_text)) { 188 | $dxp_text = sprintf( 189 | __('Open Source DXP %1$s installed.', 'osdxp-dashboard'), 190 | OSDXP_DASHBOARD_VER 191 | ); 192 | } 193 | 194 | // Set the WP version text. 195 | if (current_user_can('update_core')) { 196 | $version = get_preferred_from_update_core(); 197 | 198 | if (!is_object($version)) { 199 | $version = new \stdClass(); 200 | } 201 | 202 | if (!isset($version->current)) { 203 | $version->current = ''; 204 | } 205 | 206 | if (!isset($version->url)) { 207 | $version->url = ''; 208 | } 209 | 210 | if (!isset($version->response)) { 211 | $version->response = ''; 212 | } 213 | 214 | if ('development' === $version->response) { 215 | $wp_text = sprintf( 216 | // Translators: %1$s: WordPress version number, %2$s: WordPress updates admin screen URL. 217 | esc_html__( 218 | 'You are using a development version (%1$s). Cool! Please stay updated.', 219 | 'osdxp-dashboard' 220 | ), 221 | get_bloginfo('version', 'display'), 222 | network_admin_url('update-core.php') 223 | ); 224 | } elseif ('upgrade' === $version->response) { 225 | $wp_text = sprintf( 226 | wp_kses( 227 | // phpcs:ignore Translators: %1$s: installed WordPress version, %2$s: WP core update page, %3$s: latest WordPress version. 228 | __('WordPress %1$s installed. Get current version, %3$s.', 'osdxp-dashboard'), 229 | [ 230 | 'a' => [ 231 | 'href' => [], 232 | ], 233 | ] 234 | ), 235 | get_bloginfo('version', 'display'), 236 | network_admin_url('update-core.php'), 237 | $version->current 238 | ); 239 | } 240 | } 241 | 242 | // WP version text fallback. 243 | if (empty($wp_text)) { 244 | $wp_text = sprintf( 245 | // Translators: %s: installed WordPress version. 246 | esc_html__('WordPress %s installed.', 'osdxp-dashboard'), 247 | get_bloginfo('version', 'display') 248 | ); 249 | } 250 | 251 | return $dxp_text . '
' . $wp_text; 252 | } 253 | 254 | /** 255 | * Method to remove admin color theme picker if on osDXP. 256 | * 257 | * @return void 258 | */ 259 | function limit_admin_color_options() 260 | { 261 | if (!is_dxp_dashboard()) { 262 | return; 263 | } 264 | global $_wp_admin_css_colors; 265 | 266 | $fresh_color_data = $_wp_admin_css_colors['fresh']; 267 | $fresh_color_data->icon_colors = [ 268 | 'base' => '#fff', 269 | 'focus' => '#3C15B4', 270 | 'current' => '#3C15B4', 271 | ]; 272 | 273 | $_wp_admin_css_colors = array( 'fresh' => $fresh_color_data ); 274 | } 275 | 276 | /** 277 | * Method to force color theme if on osDXP. 278 | * 279 | * @param $color string of color theme 280 | * @return string 281 | */ 282 | function force_user_color($color) 283 | { 284 | return is_dxp_dashboard() ? 'fresh' : $color; 285 | } 286 | -------------------------------------------------------------------------------- /includes/dashboard.php: -------------------------------------------------------------------------------- 1 | '; 80 | 81 | foreach ($news_items as $item) : 82 | $title = sprintf( 83 | // Translators: %s the date when the item was posted. 84 | esc_html__('Posted %s', 'osdxp-dashboard'), 85 | $item->get_date('j F Y | g:i a') 86 | ); 87 | ?> 88 |
  • 89 | 91 | get_title()); ?> 92 | 93 |
  • 94 | '; 98 | } else { 99 | esc_html_e('There are no osDXP news at the moment.', 'osdxp-dashboard'); 100 | } 101 | } 102 | 103 | /** 104 | * Method to get DXP News items. 105 | * 106 | * @return array|bool|null 107 | */ 108 | function get_dxp_news_items() 109 | { 110 | // Get RSS Feed(s) 111 | require_once ABSPATH . WPINC . '/feed.php'; 112 | 113 | // Get a SimplePie feed object from the specified feed source. 114 | $rss = fetch_feed(OSDXP_DASHBOARD_NEWS_RSS_URL); 115 | 116 | if (is_wp_error($rss)) { 117 | return false; 118 | } 119 | 120 | // Figure out how many total items there are, but limit it to the max items count. 121 | $max_items_count = $rss->get_item_quantity(OSDXP_DASHBOARD_NEWS_RSS_MAX_ITEMS_COUNT); 122 | 123 | // Build an array of all the items, starting with element 0 (first element). 124 | return $rss->get_items(0, $max_items_count); 125 | } 126 | 127 | /** 128 | * Method to remove the WP Events and News dashboard widget. 129 | * 130 | * @return void 131 | */ 132 | function remove_wp_events_and_news_dashboard_widget() 133 | { 134 | if (is_dxp_dashboard()) { 135 | global $wp_meta_boxes; 136 | 137 | unset($wp_meta_boxes['dashboard']['side']['core']['dashboard_primary']); 138 | unset($wp_meta_boxes['dashboard-network']['side']['core']['dashboard_primary']); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /includes/dependencies/wordpress/class-osdxp-bulk-module-upgrader-skin.php: -------------------------------------------------------------------------------- 1 | upgrader->strings['skin_before_update_header'] = __('Updating Module %1$s (%2$d/%3$d)'); 29 | } 30 | 31 | 32 | /** 33 | * @param string $title 34 | */ 35 | public function before($title = '') 36 | { 37 | parent::before($this->module_info['Title']); 38 | } 39 | 40 | /** 41 | * @param string $title 42 | */ 43 | public function after($title = '') 44 | { 45 | parent::after($this->module_info['Title']); 46 | $this->decrement_update_count('plugin'); 47 | } 48 | 49 | /** 50 | */ 51 | public function bulk_footer() 52 | { 53 | parent::bulk_footer(); 54 | $update_actions = array( 55 | 'modules_page' => '' . __('Return to Installed Modules page') . '', 56 | 'available_modules_page' => '' . __('Return to Available Modules page') . '', 57 | ); 58 | if (! current_user_can('activate_plugins')) { 59 | unset($update_actions['modules_page']); 60 | } 61 | 62 | /** 63 | * Filters the list of action links available following bulk plugin updates. 64 | * 65 | * @since 3.0.0 66 | * 67 | * @param string[] $update_actions Array of plugin action links. 68 | * @param array $plugin_info Array of information for the last-updated plugin. 69 | */ 70 | $update_actions = apply_filters('update_bulk_plugins_complete_actions', $update_actions, $this->module_info); 71 | 72 | if (! empty($update_actions)) { 73 | $this->feedback(implode(' | ', (array) $update_actions)); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /includes/dependencies/wordpress/class-osdxp-module-installer-skin.php: -------------------------------------------------------------------------------- 1 | 'upload', 29 | 'url' => '', 30 | 'plugin' => '', 31 | 'nonce' => '', 32 | 'title' => '', 33 | ); 34 | $args = wp_parse_args($args, $defaults); 35 | 36 | $this->type = $args['type']; 37 | $this->api = isset($args['api']) ? $args['api'] : array(); 38 | 39 | parent::__construct($args); 40 | } 41 | 42 | 43 | public function before() 44 | { 45 | if (! empty($this->api)) { 46 | /* translators: 1: name of API, 2: version of API */ 47 | $this->upgrader->strings['process_success'] = sprintf(__('Successfully installed the module %1$s %2$s.'), $this->api->name, $this->api->version); 48 | } 49 | } 50 | 51 | 52 | public function after() 53 | { 54 | $module_file = $this->upgrader->plugin_info(); 55 | 56 | $install_actions = array(); 57 | 58 | $install_actions['activate_plugin'] = '' . __('Activate Module') . ''; 59 | 60 | if (is_multisite() && current_user_can('manage_network_plugins')) { 61 | $install_actions['network_activate'] = '' . __('Network Activate') . ''; 62 | unset($install_actions['activate_plugin']); 63 | } 64 | 65 | $install_actions['plugins_page'] = '' . __('Return to Installed Modules page') . ''; 66 | 67 | if (! $this->result || is_wp_error($this->result)) { 68 | unset($install_actions['activate_plugin'], $install_actions['network_activate']); 69 | } elseif (! current_user_can('activate_plugin', $module_file)) { 70 | unset($install_actions['activate_plugin']); 71 | } 72 | 73 | /** 74 | * Filters the list of action links available following a single module installation. 75 | * 76 | * @param string[] $install_actions Array of module action links. 77 | * @param object $api Object containing WordPress.org API module data. Empty 78 | * for non-API installs, such as when a module is installed 79 | * via upload. 80 | * @param string $module_file Path to the module file relative to the modules directory. 81 | */ 82 | $install_actions = apply_filters('install_plugin_complete_actions', $install_actions, $this->api, $module_file); 83 | 84 | if (! empty($install_actions)) { 85 | $this->feedback(implode(' ', (array) $install_actions)); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /includes/dependencies/wordpress/class-osdxp-module-upgrader-skin.php: -------------------------------------------------------------------------------- 1 | '', 34 | 'module' => '', 35 | 'nonce' => '', 36 | 'title' => __('Update Module'), 37 | ); 38 | $args = wp_parse_args($args, $defaults); 39 | 40 | $this->module = $args['module']; 41 | 42 | $this->module_active = is_plugin_active($this->module); 43 | $this->module_network_active = is_plugin_active_for_network($this->module); 44 | 45 | parent::__construct($args); 46 | } 47 | 48 | public function header() 49 | { 50 | if ($this->done_header) { 51 | return; 52 | } 53 | $this->done_header = true; 54 | echo '
    '; 55 | echo '

    ' . esc_html__('Update Module') . '

    '; 56 | } 57 | 58 | public function after() 59 | { 60 | $this->module = $this->upgrader->plugin_info(); 61 | if (! empty($this->module) && ! is_wp_error($this->result) && $this->module_active) { 62 | if ('osdxp-dashboard/osdxp-dashboard.php' === $this->module) { 63 | echo ''; // phpcs:ignore 64 | } else { 65 | // Currently used only when JS is off for a single plugin update? 66 | echo ''; // phpcs:ignore 67 | } 68 | } 69 | 70 | $this->decrement_update_count('plugin'); 71 | if ('osdxp-dashboard/osdxp-dashboard.php' === $this->module) { 72 | $update_actions = array( 73 | 'activate_module' => '' . __('Activate Module') . '', // phpcs:ignore 74 | 'plugins_page' => '' . __('Return to Modules page') . '', // phpcs:ignore 75 | ); 76 | } else { 77 | $update_actions = array( 78 | 'activate_module' => '' . __('Activate Module') . '', // phpcs:ignore 79 | 'plugins_page' => '' . __('Return to Modules page') . '', // phpcs:ignore 80 | ); 81 | } 82 | 83 | if ($this->module_active || ! $this->result || is_wp_error($this->result) || ! current_user_can('activate_plugin', $this->module)) { // phpcs:ignore 84 | unset($update_actions['activate_module']); 85 | } 86 | 87 | /** 88 | * Filters the list of action links available following a single plugin update. 89 | * 90 | * @since 2.7.0 91 | * 92 | * @param string[] $update_actions Array of plugin action links. 93 | * @param string $plugin Path to the plugin file relative to the plugins directory. 94 | */ 95 | $update_actions = apply_filters('update_plugin_complete_actions', $update_actions, $this->module); 96 | 97 | if (! empty($update_actions)) { 98 | $this->feedback(implode(' | ', (array) $update_actions)); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /includes/dependencies/wordpress/module-install.php: -------------------------------------------------------------------------------- 1 | per_page)) { 20 | $args->per_page = 24; 21 | } 22 | } 23 | 24 | if (! isset($args->locale)) { 25 | $args->locale = get_user_locale(); 26 | } 27 | 28 | if (! isset($args->wp_version)) { 29 | $args->wp_version = substr($wp_version, 0, 3); // X.y 30 | } 31 | 32 | /** 33 | * Filters the WordPress.org Plugin Installation API arguments. 34 | * 35 | * Important: An object MUST be returned to this filter. 36 | * 37 | * @since 2.7.0 38 | * 39 | * @param object $args Plugin API arguments. 40 | * @param string $action The type of information being requested from the Plugin Installation API. 41 | */ 42 | $args = apply_filters('plugins_api_args', $args, $action); 43 | 44 | /** 45 | * Filters the response for the current WordPress.org Plugin Installation API request. 46 | * 47 | * Passing a non-false value will effectively short-circuit the WordPress.org API request. 48 | * 49 | * If `$action` is 'query_plugins' or 'plugin_information', an object MUST be passed. 50 | * If `$action` is 'hot_tags' or 'hot_categories', an array should be passed. 51 | * 52 | * @since 2.7.0 53 | * 54 | * @param false|object|array $result The result object or array. Default false. 55 | * @param string $action The type of information being requested from the Plugin Installation API. 56 | * @param object $args Plugin API arguments. 57 | */ 58 | $res = apply_filters('plugins_api', false, $action, $args); 59 | 60 | if (false === $res) { 61 | $url = 'http://api.wordpress.org/plugins/info/1.2/'; 62 | $url = add_query_arg( 63 | array( 64 | 'action' => $action, 65 | 'request' => $args, 66 | ), 67 | $url 68 | ); 69 | 70 | $http_url = $url; 71 | if ($ssl = wp_http_supports(array( 'ssl' ))) { 72 | $url = set_url_scheme($url, 'https'); 73 | } 74 | 75 | $http_args = array( 76 | 'timeout' => 15, 77 | 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url('/'), 78 | ); 79 | $request = wp_remote_get($url, $http_args); 80 | 81 | if ($ssl && is_wp_error($request)) { 82 | trigger_error( 83 | sprintf( 84 | /* translators: %s: support forums URL */ 85 | __('An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.'), 86 | __('https://wordpress.org/support/') 87 | ) . ' ' . __('(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)'), 88 | headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE 89 | ); 90 | $request = wp_remote_get($http_url, $http_args); 91 | } 92 | 93 | if (is_wp_error($request)) { 94 | $res = new WP_Error( 95 | 'plugins_api_failed', 96 | sprintf( 97 | /* translators: %s: support forums URL */ 98 | __('An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.'), 99 | __('https://wordpress.org/support/') 100 | ), 101 | $request->get_error_message() 102 | ); 103 | } else { 104 | $res = json_decode(wp_remote_retrieve_body($request), true); 105 | if (is_array($res)) { 106 | // Object casting is required in order to match the info/1.0 format. 107 | $res = (object) $res; 108 | } elseif (null === $res) { 109 | $res = new WP_Error( 110 | 'plugins_api_failed', 111 | sprintf( 112 | /* translators: %s: support forums URL */ 113 | __('An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.'), 114 | __('https://wordpress.org/support/') 115 | ), 116 | wp_remote_retrieve_body($request) 117 | ); 118 | } 119 | 120 | if (isset($res->error)) { 121 | $res = new WP_Error('plugins_api_failed', $res->error); 122 | } 123 | } 124 | } elseif (! is_wp_error($res)) { 125 | $res->external = true; 126 | } 127 | 128 | /** 129 | * Filters the Plugin Installation API response results. 130 | * 131 | * @since 2.7.0 132 | * 133 | * @param object|WP_Error $res Response object or WP_Error. 134 | * @param string $action The type of information being requested from the Plugin Installation API. 135 | * @param object $args Plugin API arguments. 136 | */ 137 | return apply_filters('plugins_api_result', $res, $action, $args); 138 | } 139 | 140 | /** 141 | * Determine the status we can perform on a plugin. 142 | * 143 | * @since 3.0.0 144 | * 145 | * @param array|object $api Data about the plugin retrieved from the API. 146 | * @param bool $loop Optional. Disable further loops. Default false. 147 | * @return array { 148 | * Plugin installation status data. 149 | * 150 | * @type string $status Status of a plugin. 151 | * Could be one of 'install', 'update_available', 'latest_installed' or 'newer_installed'. 152 | * @type string $url Plugin installation URL. 153 | * @type string $version The most recent version of the plugin. 154 | * @type string $file Plugin filename relative to the plugins directory. 155 | * } 156 | */ 157 | function install_plugin_install_status($api, $loop = false) 158 | { 159 | // This function is called recursively, $loop prevents further loops. 160 | if (is_array($api)) { 161 | $api = (object) $api; 162 | } 163 | 164 | // Default to a "new" plugin 165 | $status = 'install'; 166 | $url = false; 167 | $update_file = false; 168 | $version = ''; 169 | 170 | /* 171 | * Check to see if this plugin is known to be installed, 172 | * and has an update awaiting it. 173 | */ 174 | $update_plugins = get_site_transient('update_plugins'); 175 | if (isset($update_plugins->response)) { 176 | foreach ((array) $update_plugins->response as $file => $plugin) { 177 | if ($plugin->slug === $api->slug) { 178 | $status = 'update_available'; 179 | $update_file = $file; 180 | $version = $plugin->new_version; 181 | if (current_user_can('update_plugins')) { 182 | $url = wp_nonce_url( 183 | self_admin_url( 184 | 'admin.php?page=dxp-modules-update&action=upgrade-module&module=' . $update_file 185 | ), 186 | 'upgrade-module_' . $update_file 187 | ); 188 | } 189 | break; 190 | } 191 | } 192 | } 193 | if (isset($_GET['from'])) { 194 | $url .= '&from=' . urlencode(wp_unslash($_GET['from'])); 195 | } 196 | 197 | $file = $update_file; 198 | return compact('status', 'url', 'version', 'file'); 199 | } 200 | -------------------------------------------------------------------------------- /includes/dependencies/wordpress/modules-print-updates.php: -------------------------------------------------------------------------------- 1 | response) && is_array($modules->response)) { 24 | $modules = array_keys($modules->response); 25 | foreach ($modules as $module_file) { 26 | add_action("after_plugin_row_$module_file", 'osdxp_module_update_row', 10, 2); 27 | } 28 | } 29 | } 30 | 31 | /** 32 | * Displays update information for a module. 33 | * 34 | * @see wp_plugin_update_row 35 | * @param string $file Module basename. 36 | * @param array $module_data Module information. 37 | * @return false|void 38 | */ 39 | function osdxp_module_update_row($file, $module_data) 40 | { 41 | $current = get_site_transient('update_plugins'); 42 | if (! isset($current->response[ $file ])) { 43 | return false; 44 | } 45 | 46 | $response = $current->response[ $file ]; 47 | 48 | $modules_allowedtags = array( 49 | 'a' => array( 50 | 'href' => array(), 51 | 'title' => array(), 52 | ), 53 | 'abbr' => array( 'title' => array() ), 54 | 'acronym' => array( 'title' => array() ), 55 | 'code' => array(), 56 | 'em' => array(), 57 | 'strong' => array(), 58 | ); 59 | 60 | $module_name = wp_kses($module_data['Name'], $modules_allowedtags); 61 | $details_url = esc_url($response->url); 62 | 63 | /** @var OSDXP_Modules_List_Table $osdxp_modules_list_table */ 64 | $osdxp_modules_list_table = new OSDXP_Dashboard\OSDXP_Modules_List_Table(); 65 | 66 | if (is_network_admin() || ! is_multisite()) { 67 | if (is_network_admin()) { 68 | $active_class = is_plugin_active_for_network($file) ? ' active' : ''; 69 | } else { 70 | $active_class = is_plugin_active($file) ? ' active' : ''; 71 | } 72 | 73 | $requires_php = isset($response->requires_php) ? $response->requires_php : null; 74 | $compatible_php = is_php_version_compatible($requires_php); 75 | $notice_type = $compatible_php ? 'notice-warning' : 'notice-error'; 76 | 77 | echo '

    '; 78 | 79 | if (!current_user_can('update_plugins')) { 80 | /* translators: 1: module name, 2: details URL, 3: additional link attributes, 4: version number */ 81 | printf( 82 | __('There is a new version of %1$s available. View version %4$s details.'), 83 | $module_name, 84 | esc_url($details_url), 85 | 'target="_blank"', 86 | esc_attr($response->new_version) 87 | ); 88 | } elseif (empty($response->package)) { 89 | /* translators: 1: module name, 2: details URL, 3: additional link attributes, 4: version number */ 90 | printf( 91 | __('There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this module.'), 92 | $module_name, 93 | esc_url($details_url), 94 | 'target="_blank"', 95 | esc_attr($response->new_version) 96 | ); 97 | } else { 98 | if ($compatible_php) { 99 | /* translators: 1: module name, 2: details URL, 3: additional link attributes, 4: version number, 5: update URL, 6: additional link attributes */ 100 | printf( 101 | __('There is a new version of %1$s available. View version %4$s details or update now.'), 102 | $module_name, 103 | esc_url($details_url), 104 | 'target="_blank"', 105 | esc_attr($response->new_version), 106 | wp_nonce_url(self_admin_url('admin.php?page=dxp-modules-update&noheader&action=upgrade-module&module=') . $file, 'upgrade-module_' . $file), 107 | sprintf( 108 | 'class="update-link" aria-label="%s"', 109 | /* translators: %s: module name */ 110 | esc_attr(sprintf(__('Update %s now'), $module_name)) 111 | ) 112 | ); 113 | } else { 114 | /* translators: 1: module name, 2: details URL, 3: additional link attributes, 4: version number 5: Update PHP page URL */ 115 | printf( 116 | __('There is a new version of %1$s available, but it doesn’t work with your version of PHP. View version %4$s details or learn more about updating PHP.'), 117 | $module_name, 118 | esc_url($details_url), 119 | 'target="_blank"', 120 | esc_attr($response->new_version), 121 | esc_url(wp_get_update_php_url()) 122 | ); 123 | wp_update_php_annotation('
    ', ''); 124 | } 125 | } 126 | 127 | /** 128 | * Fires at the end of the update message container in each 129 | * row of the modules list table. 130 | * 131 | * The dynamic portion of the hook name, `$file`, refers to the path 132 | * of the modules's primary file relative to the modules directory. 133 | * 134 | * 135 | * @param array $module_data { 136 | * An array of module metadata. 137 | * 138 | * @type string $name The human-readable name of the module. 139 | * @type string $module_uri Module URI. 140 | * @type string $version Module version. 141 | * @type string $description Module description. 142 | * @type string $author Module author. 143 | * @type string $author_uri Module author URI. 144 | * @type string $text_domain Module text domain. 145 | * @type string $domain_path Relative path to the module's .mo file(s). 146 | * @type bool $network Whether the module can only be activated network wide. 147 | * @type string $title The human-readable title of the module. 148 | * @type string $author_name Module author's name. 149 | * @type bool $update Whether there's an available update. Default null. 150 | * } 151 | * @param array $response { 152 | * An array of metadata about the available module update. 153 | * 154 | * @type int $id Module ID. 155 | * @type string $slug Module slug. 156 | * @type string $new_version New module version. 157 | * @type string $url Module URL. 158 | * @type string $package Module update package URL. 159 | * } 160 | */ 161 | do_action("in_plugin_update_message-{$file}", $module_data, $response); 162 | 163 | echo '

    '; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /includes/modules.php: -------------------------------------------------------------------------------- 1 | $plugin_data) { 32 | $name_key = array_search($plugin_data['Name'], $module_names); 33 | $module_name = (false !== $name_key) ? $module_names[$name_key] : in_array($slug, $module_slugs, true); 34 | 35 | // An osDXP module but not osDXP dashboard. 36 | if ($module_name && 'Open Source DXP Dashboard' !== $module_name) { 37 | unset($plugins[$slug]); 38 | } 39 | } 40 | 41 | return $plugins; 42 | } 43 | 44 | /** 45 | * Method to display DXP modules 46 | * 47 | * @param array $plugins Plugins array. 48 | * 49 | * @return array 50 | * 51 | * @see class-osdxp-modules-list.php apply_filters( 'osdxp_get_modules', get_plugins() ) 52 | */ 53 | function filter_modules($plugins) 54 | { 55 | if (!$plugins || !is_array($plugins)) { 56 | return []; 57 | } 58 | 59 | $modules = get_osdxp_available_modules(); 60 | $module_names = array_combine(array_keys($modules), array_column($modules, 'name')); 61 | 62 | foreach ($plugins as $slug => $module_data) { 63 | if (isset($modules[$slug])) { 64 | $available_module_data = $modules[$slug]; 65 | } elseif (false !== $name_key = array_search($module_data['Name'], $module_names)) { 66 | $available_module_data = $modules[$name_key]; 67 | } else { 68 | $available_module_data = false; 69 | } 70 | 71 | // Not an OSDXP module. 72 | if (false === $available_module_data) { 73 | unset($plugins[$slug]); 74 | continue; 75 | } elseif (!empty($available_module_data['logo'])) { 76 | $module_data['logo'] = $available_module_data['logo']; 77 | } 78 | 79 | //set name from endpoint 80 | $module_data['Name'] = $available_module_data['name']; 81 | //set logo to placeholder if missing 82 | $module_data['logo'] = empty($module_data['logo']) 83 | ? OSDXP_DASHBOARD_PLACEHOLDER_IMAGE_URL 84 | : esc_url($module_data['logo']); 85 | $module_data['logo'] = ''; 86 | 87 | $plugins[$slug] = $module_data; 88 | } 89 | 90 | return $plugins; 91 | } 92 | 93 | /** 94 | * Method to return an array of modules names. 95 | * 96 | * @return array 97 | */ 98 | function get_osdxp_available_modules() 99 | { 100 | $osdxp_available_module = new OSDXPAvailableModules(); 101 | $available_modules_json = $osdxp_available_module->getModulesData(); 102 | $available_modules_array = $osdxp_available_module->transformData($available_modules_json); 103 | 104 | if (!$available_modules_array || !is_array($available_modules_array)) { 105 | $available_modules_array = []; 106 | } 107 | 108 | if (!in_array(OSDXP_DASHBOARD_PLUGIN_NAME, array_column($available_modules_array, 'name'))) { 109 | $available_modules_array[OSDXP_DASHBOARD_PLUGIN_BASENAME] = [ 110 | 'name' => OSDXP_DASHBOARD_PLUGIN_NAME, 111 | 'logo' => OSDXP_DASHBOARD_PLUGIN_LOGO 112 | ]; 113 | } 114 | 115 | return $available_modules_array; 116 | } 117 | -------------------------------------------------------------------------------- /includes/notifications.php: -------------------------------------------------------------------------------- 1 | 44 |
    45 |

    46 | 47 | 48 |

    49 |
    50 |
    51 | 52 |
    53 | "#{$drawer_id}", 69 | 'selectorNotice' => "#{$notice_id}", 70 | 'threshold' => max(1, $threshold), 71 | ]); 72 | }, PHP_INT_MAX); 73 | } 74 | // phpcs:disable 75 | add_action('plugins_loaded', __NAMESPACE__ . '\\notifications'); 76 | // phpcs:enable 77 | -------------------------------------------------------------------------------- /includes/utils.php: -------------------------------------------------------------------------------- 1 | getModulesData(); 151 | } 152 | ); 153 | } 154 | 155 | /** 156 | * Redirect to dashboard on plugin activation 157 | * Delete option for cleanup 158 | * 159 | * @see osdxp-dashboard.php 160 | * @see osdxp_activate in utils.php 161 | * 162 | * @return void 163 | */ 164 | function osdxp_activation_redirect() 165 | { 166 | //ignore multisite since it will always be dxp on network dashboard 167 | if (get_option('osdxp_activation_redirect', false) && !is_network_admin()) { 168 | delete_option('osdxp_activation_redirect'); 169 | wp_safe_redirect(esc_url(self_admin_url('?dxp=on'))); 170 | exit; 171 | } 172 | } 173 | 174 | /** 175 | * Method to return the escaped license field markup. 176 | * 177 | * @param string $license_field_markup License field markup. 178 | * 179 | * @return string 180 | */ 181 | function esc_license_field_markup($license_field_markup) 182 | { 183 | return wp_kses( 184 | $license_field_markup, 185 | [ 186 | 'button' => [ 187 | 'class' => [], 188 | ], 189 | 'div' => [ 190 | 'class' => [], 191 | ], 192 | 'input' => [ 193 | 'class' => [], 194 | 'data-module' => [], 195 | 'id' => [], 196 | 'size' => [], 197 | 'type' => [], 198 | ], 199 | 'label' => [ 200 | 'for' => [], 201 | ], 202 | 'strong' => [], 203 | ] 204 | ); 205 | } 206 | 207 | /** 208 | * Method to return the escaped license key markup. 209 | * 210 | * @param string $license_key_markup License key markup. 211 | * 212 | * @return string 213 | */ 214 | function esc_license_key_markup($license_key_markup) 215 | { 216 | return wp_kses( 217 | $license_key_markup, 218 | [ 219 | 'a' => [ 220 | 'class' => [], 221 | 'data-module' => [], 222 | 'href' => [], 223 | ], 224 | 'p' => [ 225 | 'class' => [], 226 | ], 227 | 'strong' => [], 228 | ] 229 | ); 230 | } 231 | 232 | /** 233 | * Extend array_change_key_case for multidimensional arrays 234 | * 235 | * @param array $arr multidimensional array 236 | * 237 | * @return array $array with keys changed 238 | */ 239 | function array_change_key_case_recursive($arr) 240 | { 241 | return array_map(function ($item) { 242 | if (is_array($item)) { 243 | $item = array_change_key_case_recursive($item); 244 | } 245 | return $item; 246 | }, array_change_key_case($arr)); 247 | } 248 | -------------------------------------------------------------------------------- /module_update_info.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Open Source DXP Dashboard", 3 | "version": "2.0", 4 | "download_url": "http://example.com/updates/example-plugin.zip", 5 | 6 | "homepage": "https://applications.crowdfavorite.com/", 7 | "requires": "4.5", 8 | "tested": "4.8", 9 | "last_updated": "2019-07-23 10:17:00", 10 | "upgrade_notice": "Here's why you should upgrade...", 11 | 12 | "author": "Crowd Favorite", 13 | "author_homepage": "http://crowdfavorite.com/", 14 | 15 | "sections": { 16 | "description": "(Required) Plugin description. Basic HTML can be used in all sections.", 17 | "installation": "(Recommended) Installation instructions.", 18 | "changelog": "(Recommended) Changelog.

    This section will be displayed by default when the user clicks 'View version x.y.z details'.

    ", 19 | "custom_section": "This is a custom section labeled 'Custom Section'." 20 | }, 21 | 22 | "icons" : { 23 | "1x" : "http://w-shadow.com/files/external-update-example/assets/icon-128x128.png", 24 | "2x" : "http://w-shadow.com/files/external-update-example/assets/icon-256x256.png" 25 | }, 26 | 27 | "banners": { 28 | "low": "http://w-shadow.com/files/external-update-example/assets/banner-772x250.png", 29 | "high": "http://w-shadow.com/files/external-update-example/assets/banner-1544x500.png" 30 | }, 31 | 32 | "translations": [ 33 | { 34 | "language": "fr_FR", 35 | "version": "4.0", 36 | "updated": "2016-04-22 23:22:42", 37 | "package": "http://example.com/updates/translations/french-language-pack.zip" 38 | }, 39 | { 40 | "language": "de_DE", 41 | "version": "5.0", 42 | "updated": "2016-04-22 23:22:42", 43 | "package": "http://example.com/updates/translations/german-language-pack.zip" 44 | } 45 | ], 46 | 47 | "rating": 90, 48 | "num_ratings": 123, 49 | 50 | "downloaded": 1234, 51 | "active_installs": 12345 52 | } -------------------------------------------------------------------------------- /osdxp-dashboard.php: -------------------------------------------------------------------------------- 1 | =4.5.0", 53 | "node-bourbon": "~4.2.8", 54 | "node-sass": "^4.14.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Custom variant of PSR12 with WordPress security checks for PHP development 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | */node_modules/* 52 | */vendor/* 53 | */dependencies/* 54 | 55 | 56 | . 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /templates/dashboard-actions-editor.php: -------------------------------------------------------------------------------- 1 | 10 | 141 | -------------------------------------------------------------------------------- /templates/dashboard-actions-network-admin.php: -------------------------------------------------------------------------------- 1 | 10 | 156 | -------------------------------------------------------------------------------- /templates/dashboard-actions-single-admin.php: -------------------------------------------------------------------------------- 1 | 10 | 171 | -------------------------------------------------------------------------------- /templates/modules-information.php: -------------------------------------------------------------------------------- 1 | getModulesData(); 8 | $data = $this->transformData($jsonData); 9 | } 10 | ?> 11 | 12 |

    13 | 14 | 15 | 16 | 17 |

    18 | 19 |

    20 | 21 |
    22 | $module_info) {?> 25 |
    26 |
    27 |
    28 |
    29 | 40 | 41 | 42 | 43 | 44 | 45 |
    46 |
    47 |

    48 |

    49 |

    :

    50 |
    51 |
    52 |
    53 |
    54 |
    55 | 56 | 61 | 62 |
    63 |
    64 | 65 | 70 | 71 |
    72 |
    73 | 74 | 79 | 80 |
    81 |
    82 | 87 | 88 | 89 |
    90 |
    91 |
    92 | 93 |
    94 | 101 |
    102 | 106 |
    107 | -------------------------------------------------------------------------------- /templates/modules-update.php: -------------------------------------------------------------------------------- 1 | bulk_upgrade($modules); 55 | 56 | iframe_footer(); 57 | } elseif ('upgrade-module' == $action) { 58 | if (! current_user_can('update_plugins')) { 59 | wp_die(esc_html__('Sorry, you are not allowed to update modules for this site.')); 60 | } 61 | 62 | check_admin_referer('upgrade-module_' . $module); 63 | 64 | $title = esc_html__('Update Module'); 65 | $parent_file = 'admin.php?page=dxp-modules'; 66 | $submenu_file = 'admin.php?page=dxp-modules-installed'; 67 | 68 | // wp_enqueue_script( 'updates' ); 69 | require_once(ABSPATH . 'wp-admin/admin-header.php'); 70 | 71 | $nonce = 'upgrade-module_' . $module; 72 | $url = 'admin.php?page=dxp-modules-update&noheader&action=upgrade-module&module=' . urlencode($module); 73 | 74 | $upgrader = new OSDXP_Module_Upgrader( 75 | new OSDXP_Module_Upgrader_Skin(compact('title', 'nonce', 'url', 'module')) 76 | ); 77 | $upgrader->upgrade($module); 78 | 79 | include(ABSPATH . 'wp-admin/admin-footer.php'); 80 | } elseif ('activate-module' == $action) { 81 | if (! current_user_can('update_plugins')) { 82 | wp_die(esc_html__('Sorry, you are not allowed to update modules for this site.')); 83 | } 84 | 85 | check_admin_referer('activate-module_' . $module); 86 | if (! isset($_GET['failure']) && ! isset($_GET['success'])) { 87 | wp_redirect( 88 | admin_url('admin.php?page=dxp-modules-update&noheader&action=activate-module&failure=true&module=' . urlencode($module) . '&_wpnonce=' . $_GET['_wpnonce']) // phpcs:ignore 89 | ); 90 | activate_plugin($module, '', ! empty($_GET['networkwide']), true); 91 | wp_redirect( 92 | admin_url('admin.php?page=dxp-modules-update&noheader&action=activate-module&success=true&module=' . urlencode($module) . '&_wpnonce=' . $_GET['_wpnonce']) // phpcs:ignore 93 | ); 94 | die(); 95 | } 96 | iframe_header(esc_html__('Module Reactivation'), true); 97 | if (isset($_GET['success'])) { 98 | echo '

    ' . esc_html__('Module reactivated successfully.') . '

    '; 99 | } 100 | 101 | if (isset($_GET['failure'])) { 102 | echo '

    ' . esc_html__('Module failed to reactivate due to a fatal error.') . '

    '; 103 | 104 | error_reporting(E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR); // phpcs:ignore 105 | @ini_set('display_errors', true); //Ensure that Fatal errors are displayed. 106 | wp_register_plugin_realpath(WP_PLUGIN_DIR . '/' . $module); 107 | include(WP_PLUGIN_DIR . '/' . $module); 108 | } 109 | iframe_footer(); 110 | } elseif ('upload-module' == $action) { 111 | if (! current_user_can('upload_plugins')) { 112 | wp_die(esc_html__('Sorry, you are not allowed to install modules on this site.')); 113 | } 114 | 115 | check_admin_referer('module-upload'); 116 | 117 | $file_upload = new File_Upload_Upgrader('pluginzip', 'package'); 118 | 119 | $title = esc_html__('Upload Module'); 120 | $parent_file = 'admin.php?page=dxp-modules'; 121 | $submenu_file = 'admin.php?page=dxp-modules-installed'; 122 | require_once(ABSPATH . 'wp-admin/admin-header.php'); 123 | 124 | $title = sprintf( 125 | esc_html__('Installing Module from uploaded file: %s'), 126 | esc_html(basename($file_upload->filename)) 127 | ); 128 | $nonce = 'module-upload'; 129 | $url = add_query_arg( 130 | array( 'package' => $file_upload->id ), 131 | 'admin.php?page=dxp-modules-update&action=upload-module' 132 | ); 133 | $type = 'upload'; //Install module type, From Web or an Upload. 134 | 135 | $upgrader = new OSDXP_Module_Upgrader( 136 | new OSDXP_Module_Installer_Skin(compact('type', 'title', 'nonce', 'url')) 137 | ); 138 | $result = $upgrader->install($file_upload->package); 139 | 140 | if ($result || is_wp_error($result)) { 141 | $file_upload->cleanup(); 142 | } 143 | 144 | include(ABSPATH . 'wp-admin/admin-footer.php'); 145 | } else { 146 | /** 147 | * Fires when a custom module update request is received. 148 | * 149 | * The dynamic portion of the hook name, `$action`, refers to the action 150 | * provided in the request for wp-admin/update.php. Can be used to 151 | * provide custom update functionality for modules. 152 | * 153 | */ 154 | do_action("update-custom_{$action}"); 155 | } 156 | } 157 | --------------------------------------------------------------------------------