├── .env ├── .gitignore ├── .travis.yml ├── README.md ├── assets ├── css │ └── modules │ │ ├── idlikethis-button-simple.scss │ │ └── idlikethis-post-control-meta-box.scss └── js │ ├── dist │ ├── idlikethis-admin.js │ └── idlikethis.js │ ├── idlikethis-admin.js │ ├── idlikethis.js │ └── modules │ ├── admin-app.js │ ├── app.js │ ├── backbone.js │ ├── jquery.js │ ├── localized-data.js │ ├── models │ ├── ConsolidateAllCommand.js │ ├── ControlCommand.js │ ├── ResetAllCommand.js │ └── VoteCaster.js │ ├── storage.js │ ├── underscore.js │ └── views │ ├── AdminControlButton.js │ └── Button.js ├── bootstrap.php ├── build └── travis-nginx-conf ├── codeception.dist.yml ├── composer.json ├── composer.lock ├── idlikethis.php ├── package.json ├── src └── idlikethis │ ├── Adapters │ └── SmartyAdapter.php │ ├── Comments │ ├── TableContext.php │ ├── TableContextInterface.php │ ├── TableFilter.php │ └── TableFilterInterface.php │ ├── Contexts │ ├── ContextInterface.php │ ├── MetaBoxContext.php │ ├── MetaBoxContextInterface.php │ ├── ShortcodeContext.php │ └── ShortcodeContextInterface.php │ ├── Endpoints │ ├── AuthHandler.php │ ├── AuthHandlerInterface.php │ ├── ButtonClickHandler.php │ ├── ButtonClickHandlerInterface.php │ ├── ConsolidateAllHandler.php │ ├── ConsolidateAllHandlerInterface.php │ ├── HandlerInterface.php │ ├── PostAdminAuthHandler.php │ ├── PostAdminAuthHandlerInterface.php │ ├── ResetAllHandler.php │ └── ResetAllHandlerInterface.php │ ├── MetaBoxes │ ├── MetaBoxInterface.php │ ├── PostControlMetaBox.php │ ├── PostControlMetaBoxInterface.php │ ├── VotesDisplayMetaBox.php │ └── VotesDisplayMetaBoxInterface.php │ ├── Plugin.php │ ├── Repositories │ ├── CommentsRepository.php │ └── VotesRepositoryInterface.php │ ├── Scripts │ ├── BackEndDataProvider.php │ ├── BackEndDataProviderInterface.php │ ├── BackEndScriptsQ.php │ ├── BackEndScriptsQInterface.php │ ├── DataProviderInterface.php │ ├── FrontEndDataProvider.php │ ├── FrontEndDataProviderInterface.php │ ├── FrontEndScriptsQ.php │ ├── FrontEndScriptsQInterface.php │ └── ScriptsQInterface.php │ ├── ServiceProviders │ ├── CommentsTable.php │ ├── Endpoints.php │ ├── MetaBoxes.php │ ├── Scripts.php │ └── Shortcodes.php │ ├── Shortcodes │ ├── ShortcodeInterface.php │ ├── Simple.php │ └── UserContentShortcode.php │ ├── Templates │ └── RenderEngineInterface.php │ └── Texts │ ├── PostControlMetaBoxTextsProvider.php │ ├── PostControlMetaBoxTextsProviderInterface.php │ ├── ShortcodeTextProvider.php │ ├── ShortcodeTextProviderInterface.php │ ├── VotesMetaBoxTextProvider.php │ └── VotesMetaBoxTextProviderInterface.php ├── templates ├── metaboxes │ ├── post-control.tpl │ └── votes.tpl └── shortcodes │ └── simple.tpl ├── tests ├── _bootstrap.php ├── _data │ └── dump.sql ├── _support │ ├── AcceptanceTester.php │ ├── FunctionalTester.php │ ├── Helper │ │ ├── Acceptance.php │ │ ├── Functional.php │ │ ├── Integration.php │ │ └── Unit.php │ ├── IntegrationTester.php │ ├── UnitTester.php │ └── _generated │ │ ├── AcceptanceTesterActions.php │ │ ├── FunctionalTesterActions.php │ │ ├── IntegrationTesterActions.php │ │ └── UnitTesterActions.php ├── acceptance.suite.dist.yml ├── acceptance │ ├── _bootstrap.php │ └── frontend │ │ └── ShortcodeCest.php ├── functional.suite.dist.yml ├── functional │ ├── _bootstrap.php │ ├── frontend │ │ └── ShortcodeCest.php │ └── rest │ │ ├── ButtonClickPostRequestCest.php │ │ ├── ConsolidateAllPostRequestCest.php │ │ └── ResetAllPostRequestCest.php ├── integration.suite.dist.yml ├── integration │ ├── _bootstrap.php │ └── idlikethis │ │ ├── Adapters │ │ └── SmartyAdapterTest.php │ │ ├── Comments │ │ └── TableFilterTest.php │ │ ├── Endpoints │ │ ├── ButtonClickHandlerTest.php │ │ ├── ConsolidateAllHandlerTest.php │ │ ├── PostAdminAuthHandlerTest.php │ │ └── ResetAllHandlerTest.php │ │ ├── MetaBoxes │ │ ├── PostControlMetaBoxTest.php │ │ └── VotesDisplayMetaBoxTest.php │ │ ├── PluginTest.php │ │ ├── Repositories │ │ └── CommentsRepositoryTest.php │ │ └── Shortcodes │ │ ├── SimpleTest.php │ │ └── UserContentShortcodeTest.php ├── unit.suite.dist.yml └── unit │ ├── _bootstrap.php │ └── idlikethis │ └── Scripts │ ├── BackEndScriptsQTest.php │ └── FrontEndScriptsQTest.php └── webpack.config.js /.env: -------------------------------------------------------------------------------- 1 | WP_ROOT='/tmp/wordpress' 2 | WP_URL='http://wp.localhost' 3 | WP_DOMAIN='wp.localhost' 4 | DB_HOST='localhost' 5 | DB_NAME='test' 6 | TEST_DB_NAME='wploader' 7 | DB_USER='root' 8 | DB_PASSWORD='' 9 | PHANTOMJS="/usr/local/phantomjs/phantomjs" 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea 3 | 4 | ### Composer template 5 | vendor/ 6 | 7 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file 8 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 9 | # composer.lock 10 | 11 | node_modules 12 | tests/_output 13 | codeception.yml 14 | .env.local 15 | templates/cache 16 | templates/compiled -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: php 4 | 5 | notifications: 6 | email: false 7 | 8 | php: 9 | - '7.1' 10 | - nightly 11 | 12 | matrix: 13 | fast_finish: true 14 | allow_failures: 15 | - php: nightly 16 | - env: WP_VERSION=nightly 17 | 18 | services: 19 | - mysql 20 | 21 | cache: 22 | apt: true 23 | directories: 24 | - vendor 25 | - $HOME/.composer/cache/files 26 | 27 | addons: 28 | apt: 29 | packages: 30 | - libjpeg-dev 31 | - libpng12-dev 32 | - php5-fpm 33 | - php5-mysql 34 | - nginx 35 | hosts: 36 | - wp.localhost 37 | 38 | env: 39 | global: 40 | - WP_FOLDER="/tmp/wordpress" 41 | - WP_URL="http://wp.localhost" 42 | - WP_DOMAIN="wp.localhost" 43 | - DB_NAME="test" 44 | - TEST_DB_NAME="wploader" 45 | - WP_TABLE_PREFIX="wp_" 46 | - WP_ADMIN_USERNAME="admin" 47 | - WP_ADMIN_PASSWORD="admin" 48 | matrix: 49 | - WP_VERSION=latest 50 | - WP_VERSION=nightly 51 | 52 | before_install: 53 | # create the databases that will be used in the tests 54 | - mysql -e "create database IF NOT EXISTS $DB_NAME;" -uroot 55 | - mysql -e "create database IF NOT EXISTS $TEST_DB_NAME;" -uroot 56 | # set up folders 57 | - mkdir -p $WP_FOLDER 58 | - mkdir tools 59 | # install wp-cli in the `tools` folder 60 | - wget https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar -P $(pwd)/tools/ 61 | - chmod +x tools/wp-cli.phar && mv tools/wp-cli.phar tools/wp 62 | # append the `tools` folder to the PATH 63 | - export PATH=$PATH:$(pwd)/tools 64 | # prepend the `vendor/bin` folder the PATH 65 | - export PATH=vendor/bin:$PATH 66 | 67 | install: 68 | - composer install --prefer-dist 69 | # install WordPress 70 | - cd $WP_FOLDER 71 | - wp core download --version=$WP_VERSION 72 | - wp config create --dbname="$DB_NAME" --dbuser="root" --dbpass="" --dbhost="127.0.0.1" --dbprefix="$WP_TABLE_PREFIX" 73 | - wp core install --url="$WP_URL" --title="Test" --admin_user="$WP_ADMIN_USERNAME" --admin_password="$WP_ADMIN_PASSWORD" --admin_email="admin@$WP_DOMAIN" --skip-email 74 | - wp rewrite structure '/%postname%/' --hard 75 | # update WordPress database to avoid prompts 76 | - wp core update-db 77 | # copy the plugin in the WordPress plugin folder 78 | - cp -r $TRAVIS_BUILD_DIR $WP_FOLDER/wp-content/plugins/idlikethis 79 | # show the plugins folder contents to make sure the plugin folder is there 80 | - ls $WP_FOLDER/wp-content/plugins 81 | # activate the plugin 82 | - wp plugin activate idlikethis 83 | # make sure the plugin is active on the site 84 | - wp plugin list --status=active 85 | # export a dump of the just installed database to the _data folder 86 | - wp db export $TRAVIS_BUILD_DIR/tests/_data/dump.sql 87 | # get back to the build folder 88 | - cd $TRAVIS_BUILD_DIR 89 | # open up the site folder to allow the PHP application to read/write/execute on it 90 | - sudo chmod -R 777 $WP_FOLDER 91 | # copy the Nginx configuration file to the available sites 92 | - sudo cp build/travis-nginx-conf /etc/nginx/sites-available/$WP_DOMAIN 93 | - sudo sed -e "s?%WP_FOLDER%?$WP_FOLDER?g" --in-place /etc/nginx/sites-available/$WP_DOMAIN 94 | - sudo sed -e "s?%WP_DOMAIN%?$WP_DOMAIN?g" --in-place /etc/nginx/sites-available/$WP_DOMAIN 95 | # enable the site 96 | - sudo ln -s /etc/nginx/sites-available/$WP_DOMAIN /etc/nginx/sites-enabled/ 97 | 98 | before_script: 99 | # restart Nnginx and PHP-FPM services 100 | - sudo service php5-fpm restart 101 | - sudo service nginx restart 102 | 103 | # build Codeception modules 104 | - codecept build 105 | 106 | script: 107 | - codecept run acceptance 108 | - codecept run functional 109 | - codecept run integration 110 | - codecept run unit 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # I'd like this 2 | 3 | ![Travis CI Build Status](https://travis-ci.org/lucatume/idlikethis.svg?branch=master) 4 | 5 | *Add an "I'd like this" button anywhere in a WordPress post and get user feedback on your ideas, proposals and plans.* 6 | 7 | This plugin was built to give a concrete example of [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection "Dependency injection - Wikipedia, the free encyclopedia") usage in WordPress with [DI52](https://github.com/lucatume/DI52) and test-driven development using [Codeception](http://codeception.com/ "Codeception - BDD-style PHP testing.") and [wp-browser](https://github.com/lucatume/wp-browser "lucatume/wp-browser · GitHub") 8 | . 9 | The test coverage is far from being complete but it contains a lot of hints and practical examples in the `/tests` folder for integration and functional testing. 10 | 11 | ## Installation 12 | Clone the plugin repository in a WordPress plugin directory: 13 | 14 | ```bash 15 | cd /var/www/wordpress/wp-content/plugins 16 | git clone lucautume/idlikethis 17 | ``` 18 | 19 | Cd into the plugin folder and install [Composer](https://getcomposer.org/) dependencies: 20 | 21 | ```bash 22 | cd idlikethis 23 | composer install 24 | ``` 25 | 26 | ## Usage 27 | Activate the plugin in WordPress plugin administration screen. 28 | The plugin has not options and will add support for the `[idlikethis]` shortcode. 29 | The shortcode can be used in its short form to generate a button that will register a generic "I'd liket this" comment: 30 | 31 | ``` 32 | How would you like me to undergo the steamy hot bucket challenge? [idlikethis] 33 | ``` 34 | 35 | Or in its long form to specify an idea: 36 | 37 | ``` 38 | Would you like me to undergo the steamy hot bucket challenge? [idlikethis]Steamy hot bucket[/idlikethis] 39 | Or rather the ice bucket challenge? [idlikethis]Ice bucket[/idlikethis] 40 | ``` 41 | -------------------------------------------------------------------------------- /assets/css/modules/idlikethis-button-simple.scss: -------------------------------------------------------------------------------- 1 | .idlikethis-button__text { 2 | font-size: 75%; 3 | } -------------------------------------------------------------------------------- /assets/css/modules/idlikethis-post-control-meta-box.scss: -------------------------------------------------------------------------------- 1 | .idlikethis-postControlMetaBox { 2 | .idlikethis-postControlMetaBox__controls { 3 | .idlikethis-postControlMetaBox__control { 4 | .button { 5 | display: block; 6 | margin: 1em auto; 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /assets/js/dist/idlikethis-admin.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) 10 | /******/ return installedModules[moduleId].exports; 11 | 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ exports: {}, 15 | /******/ id: moduleId, 16 | /******/ loaded: false 17 | /******/ }; 18 | 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | 22 | /******/ // Flag the module as loaded 23 | /******/ module.loaded = true; 24 | 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | 29 | 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | 36 | /******/ // __webpack_public_path__ 37 | /******/ __webpack_require__.p = ""; 38 | 39 | /******/ // Load entry module and return exports 40 | /******/ return __webpack_require__(0); 41 | /******/ }) 42 | /************************************************************************/ 43 | /******/ ([ 44 | /* 0 */ 45 | /***/ function(module, exports, __webpack_require__) { 46 | 47 | (function () { 48 | __webpack_require__(13); 49 | 50 | var app = __webpack_require__(15), 51 | $ = __webpack_require__(12); 52 | 53 | function bootstrap() { 54 | var nonce = app.data.endpoints.nonce, 55 | resetAllUrl = app.data.endpoints.reset_all.url, 56 | consolidateAllUrl = app.data.endpoints.consolidate_all.url, 57 | resetAllButton = new app.Views.AdminControlButton({ 58 | el: '#idlikethis-reset-all', 59 | model: new app.Models.ResetAllCommand({ nonce: nonce, url: resetAllUrl }) 60 | }), 61 | consolidateAllButton = new app.Views.AdminControlButton({ 62 | el: '#idlikethis-consolidate-all', 63 | model: new app.Models.ConsolidateAllCommand({ nonce: nonce, url: consolidateAllUrl }) 64 | }); 65 | }; 66 | 67 | $(bootstrap); 68 | })(); 69 | 70 | /***/ }, 71 | /* 1 */, 72 | /* 2 */, 73 | /* 3 */ 74 | /***/ function(module, exports) { 75 | 76 | /* 77 | MIT License http://www.opensource.org/licenses/mit-license.php 78 | Author Tobias Koppers @sokra 79 | */ 80 | // css base code, injected by the css-loader 81 | module.exports = function() { 82 | var list = []; 83 | 84 | // return the list of modules as css string 85 | list.toString = function toString() { 86 | var result = []; 87 | for(var i = 0; i < this.length; i++) { 88 | var item = this[i]; 89 | if(item[2]) { 90 | result.push("@media " + item[2] + "{" + item[1] + "}"); 91 | } else { 92 | result.push(item[1]); 93 | } 94 | } 95 | return result.join(""); 96 | }; 97 | 98 | // import a list of modules into the list 99 | list.i = function(modules, mediaQuery) { 100 | if(typeof modules === "string") 101 | modules = [[null, modules, ""]]; 102 | var alreadyImportedModules = {}; 103 | for(var i = 0; i < this.length; i++) { 104 | var id = this[i][0]; 105 | if(typeof id === "number") 106 | alreadyImportedModules[id] = true; 107 | } 108 | for(i = 0; i < modules.length; i++) { 109 | var item = modules[i]; 110 | // skip already imported module 111 | // this implementation is not 100% perfect for weird media query combinations 112 | // when a module is imported multiple times with different media queries. 113 | // I hope this will never occur (Hey this way we have smaller bundles) 114 | if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { 115 | if(mediaQuery && !item[2]) { 116 | item[2] = mediaQuery; 117 | } else if(mediaQuery) { 118 | item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; 119 | } 120 | list.push(item); 121 | } 122 | } 123 | }; 124 | return list; 125 | }; 126 | 127 | 128 | /***/ }, 129 | /* 4 */ 130 | /***/ function(module, exports, __webpack_require__) { 131 | 132 | /* 133 | MIT License http://www.opensource.org/licenses/mit-license.php 134 | Author Tobias Koppers @sokra 135 | */ 136 | var stylesInDom = {}, 137 | memoize = function(fn) { 138 | var memo; 139 | return function () { 140 | if (typeof memo === "undefined") memo = fn.apply(this, arguments); 141 | return memo; 142 | }; 143 | }, 144 | isOldIE = memoize(function() { 145 | return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase()); 146 | }), 147 | getHeadElement = memoize(function () { 148 | return document.head || document.getElementsByTagName("head")[0]; 149 | }), 150 | singletonElement = null, 151 | singletonCounter = 0, 152 | styleElementsInsertedAtTop = []; 153 | 154 | module.exports = function(list, options) { 155 | if(false) { 156 | if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); 157 | } 158 | 159 | options = options || {}; 160 | // Force single-tag solution on IE6-9, which has a hard limit on the # of