mirror of
				https://github.com/dwaxweiler/connector-mobilizon
				synced 2025-06-05 21:59:25 +02:00 
			
		
		
		
	Merge branch 'main' into requests-in-php
This commit is contained in:
		| @@ -3,26 +3,50 @@ | ||||
|     "browser": true, | ||||
|     "es2020": true | ||||
|   }, | ||||
|   "globals": { | ||||
|     "MOBILIZON_CONNECTOR": "readonly" | ||||
|   }, | ||||
|   "parser": "@babel/eslint-parser", | ||||
|   "extends": [ | ||||
|     "eslint:recommended", | ||||
|     "plugin:ava/recommended" | ||||
|     "plugin:ava/recommended", | ||||
|     "plugin:@wordpress/eslint-plugin/recommended" | ||||
|   ], | ||||
|   "parserOptions": { | ||||
|     "ecmaVersion": 11, | ||||
|     "sourceType": "module" | ||||
|     "requireConfigFile": false, | ||||
|     "sourceType": "module", | ||||
|     "babelOptions": { | ||||
|       "presets": ["@babel/preset-react"] | ||||
|     } | ||||
|   }, | ||||
|   "plugins": [ | ||||
|     "ava" | ||||
|     "ava", | ||||
|     "jsx" | ||||
|   ], | ||||
|   "rules": { | ||||
|     "import/no-unresolved": [ | ||||
|       "off" | ||||
|     ], | ||||
|     "indent": [ | ||||
|       "error", | ||||
|       2 | ||||
|     ], | ||||
|     "no-console": [ | ||||
|       "error", { | ||||
|         "allow": ["error"] | ||||
|       } | ||||
|     ], | ||||
|     "prettier/prettier": [ | ||||
|       "off" | ||||
|     ], | ||||
|     "quotes": [ | ||||
|       "error", | ||||
|       "single" | ||||
|     ], | ||||
|     "react/jsx-key": [ | ||||
|       "off" | ||||
|     ], | ||||
|     "semi": [ | ||||
|       "error", | ||||
|       "never" | ||||
|   | ||||
							
								
								
									
										23
									
								
								.github/workflows/qodana-code-quality.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.github/workflows/qodana-code-quality.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| name: Qodana | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|   pull_request: | ||||
|   push: | ||||
|     branches: | ||||
|       - main | ||||
|       - 'releases/*' | ||||
|  | ||||
| jobs: | ||||
|   qodana: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: 'Qodana Scan' | ||||
|         uses: JetBrains/qodana-action@v2022.3.2 | ||||
|         env: | ||||
|           QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} | ||||
|       - uses: github/codeql-action/upload-sarif@v2 | ||||
|         with: | ||||
|           sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json | ||||
							
								
								
									
										6
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,7 +15,7 @@ jobs: | ||||
|  | ||||
|     strategy: | ||||
|       matrix: | ||||
|         node-version: [14.x] | ||||
|         node-version: [16.x] | ||||
|         # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ | ||||
|  | ||||
|     steps: | ||||
| @@ -24,5 +24,7 @@ jobs: | ||||
|         uses: actions/setup-node@v2 | ||||
|         with: | ||||
|           node-version: ${{ matrix.node-version }} | ||||
|       - run: npm ci --omit=dev --ignore-scripts | ||||
|       - name: Upgrade NPM | ||||
|         run: npm install -g npm | ||||
|       - run: npm ci --ignore-scripts | ||||
|       - run: npm run build-prod | ||||
|   | ||||
							
								
								
									
										27
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								README.md
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ Connector for Mobilizon allows you to display the upcoming events of [Mobilizon] | ||||
|  | ||||
| More details can be found in the [WordPress Plugin Directory](https://wordpress.org/plugins/connector-mobilizon/). | ||||
|  | ||||
| The current changelog can be under [source/changelog.txt](source/changelog.txt). | ||||
| The current changelog can be found under [source/changelog.txt](source/changelog.txt). | ||||
|  | ||||
| ## Development | ||||
|  | ||||
| @@ -21,18 +21,19 @@ The current changelog can be under [source/changelog.txt](source/changelog.txt). | ||||
|  | ||||
| ### Release procedure | ||||
|  | ||||
| 1. Make sure `changelog.txt` is up-to-date. Use a new version number and copy over the new section into `readme.txt`. | ||||
| 2. Update `package.json` with the same version number. | ||||
| 3. Update the `package-lock.json`: `npm i --package-lock-only` | ||||
| 4. Build: `npm run build-prod` | ||||
| 5. Make sure screenshots are up-to-date. | ||||
| 6. Copy the built plugin into `/trunk` of SVN. | ||||
| 7. Create a new tag of the new version: `svn cp trunk tags/<version>` | ||||
| 8. Check the version number occurrences in both folders. | ||||
| 9. Commit everything together to the release SVN: `svn ci -m "release version <version>"` | ||||
| 10. Commit the new version in git with the same message. | ||||
| 11. Tag the new version: `git tag v<version>` | ||||
| 12. Push the new tag to the repository: `git push --tags` | ||||
| 1. Make sure `changelog.txt` is up-to-date. | ||||
| 2. Use a new version number and copy over the new section into `readme.txt`. | ||||
| 3. Update `package.json` with the same version number. | ||||
| 4. Update the `package-lock.json`: `npm i --package-lock-only` | ||||
| 5. Build: `npm run build-prod` | ||||
| 6. Make sure screenshots are up-to-date. | ||||
| 7. Copy the built plugin into `/trunk` of SVN. | ||||
| 8. Create a new tag of the new version: `svn cp trunk tags/<version>` | ||||
| 9. Check the version number occurrences in both folders. | ||||
| 10. Commit everything together to the release SVN: `svn ci -m "release version <version>"` Make sure to add new files beforehand. | ||||
| 11. Commit the new version in git with the same message. | ||||
| 12. Tag the new version: `git tag v<version>` | ||||
| 13. Push the new tag to the repository: `git push --tags` | ||||
|  | ||||
| ### Other commands | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/screenshot-5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/screenshot-5.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 81 KiB | 
							
								
								
									
										3
									
								
								babel.config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								babel.config.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| { | ||||
|   "presets": ["@babel/preset-env", "@babel/preset-react"] | ||||
| } | ||||
| @@ -10,6 +10,7 @@ const FOLDER_BUILD = './build' | ||||
| function injectMetadata() { | ||||
|   return src( | ||||
|     [ | ||||
|       FOLDER_BUILD + '/front/block-events-loader.js', | ||||
|       FOLDER_BUILD + '/front/events-loader.js', | ||||
|       FOLDER_BUILD + '/' + PACKAGE.name + '.php', | ||||
|       FOLDER_BUILD + '/includes/constants.php', | ||||
|   | ||||
							
								
								
									
										15329
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										15329
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										47
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "connector-mobilizon", | ||||
|   "version": "0.8.0", | ||||
|   "version": "0.11.1", | ||||
|   "description": "Display Mobilizon events in WordPress.", | ||||
|   "private": true, | ||||
|   "type": "module", | ||||
| @@ -24,30 +24,35 @@ | ||||
|   }, | ||||
|   "license": "Apache-2.0", | ||||
|   "dependencies": { | ||||
|     "graphql": "16.3.0", | ||||
|     "graphql-request": "4.1.0", | ||||
|     "luxon": "2.3.1", | ||||
|     "graphql": "16.6.0", | ||||
|     "graphql-request": "6.0.0", | ||||
|     "luxon": "3.3.0", | ||||
|     "object-hash": "3.0.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@babel/core": "7.17.8", | ||||
|     "@babel/preset-env": "7.16.11", | ||||
|     "ava": "4.1.0", | ||||
|     "babel-loader": "8.2.3", | ||||
|     "c8": "7.11.0", | ||||
|     "copy-webpack-plugin": "10.2.4", | ||||
|     "eslint": "8.11.0", | ||||
|     "eslint-plugin-ava": "13.2.0", | ||||
|     "@babel/core": "7.21.8", | ||||
|     "@babel/eslint-parser": "7.21.8", | ||||
|     "@babel/preset-env": "7.21.5", | ||||
|     "@babel/preset-react": "7.18.6", | ||||
|     "@wordpress/eslint-plugin": "14.5.0", | ||||
|     "ava": "5.2.0", | ||||
|     "babel-loader": "9.1.2", | ||||
|     "browser-env": "3.3.0", | ||||
|     "c8": "7.13.0", | ||||
|     "copy-webpack-plugin": "11.0.0", | ||||
|     "eslint": "8.40.0", | ||||
|     "eslint-plugin-ava": "14.0.0", | ||||
|     "eslint-plugin-jsx": "0.1.0", | ||||
|     "eslint-plugin-react": "7.32.2", | ||||
|     "esm": "3.2.25", | ||||
|     "gulp": "4.0.2", | ||||
|     "gulp-replace": "1.1.3", | ||||
|     "husky": "7.0.4", | ||||
|     "jsdom": "19.0.0", | ||||
|     "lint-staged": "12.3.7", | ||||
|     "prettier": "2.6.0", | ||||
|     "rimraf": "3.0.2", | ||||
|     "webpack": "5.70.0", | ||||
|     "webpack-cli": "4.9.2" | ||||
|     "gulp-replace": "1.1.4", | ||||
|     "husky": "8.0.3", | ||||
|     "lint-staged": "13.2.2", | ||||
|     "prettier": "2.8.8", | ||||
|     "rimraf": "5.0.0", | ||||
|     "webpack": "5.82.0", | ||||
|     "webpack-cli": "5.1.0" | ||||
|   }, | ||||
|   "ava": { | ||||
|     "files": [ | ||||
| @@ -58,7 +63,7 @@ | ||||
|     "niceName": "Connector for Mobilizon", | ||||
|     "phpMinimumVersion": 7.4, | ||||
|     "wordpressMinimumVersion": 5.6, | ||||
|     "wordpressTestedUpToVersion": 5.9 | ||||
|     "wordpressTestedUpToVersion": "6.2" | ||||
|   }, | ||||
|   "lint-staged": { | ||||
|     "source/**/*.js": "eslint", | ||||
|   | ||||
							
								
								
									
										2
									
								
								qodana.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								qodana.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| version: '1.0' | ||||
| linter: jetbrains/qodana-js:2022.3-eap | ||||
| @@ -1,12 +1,49 @@ | ||||
| ### [Unreleased] | ||||
| #### Added | ||||
| #### Changed | ||||
| - Update dependencies | ||||
| #### Deprecated | ||||
| #### Removed | ||||
| #### Fixed | ||||
| #### Security | ||||
|  | ||||
| ### [0.11.1] | ||||
| #### Fixed | ||||
| - Revert minimum PHP version to 7.4 to allow some more time for upgrading PHP | ||||
|  | ||||
| ### [0.11.0] | ||||
| #### Changed | ||||
| - Update dependencies | ||||
| - Confirm compatibility with WordPress 6.2 | ||||
| #### Security | ||||
| - Set minimum PHP version to oldest stable 8.0 | ||||
|  | ||||
| ### [0.10.1] | ||||
| #### Changed | ||||
| - Confirm compatibility with WordPress 6.1 | ||||
| - Update dependencies | ||||
|  | ||||
| ### [0.10.0] | ||||
| #### Added | ||||
| - Add Gutenberg events list block | ||||
| - Show loading indicator during request | ||||
| #### Changed | ||||
| - Set list style type to none and left padding to zero for all occurences | ||||
| - Move shortcut usage description into installation section in `readme.txt` | ||||
| - Update dependencies | ||||
|  | ||||
| ### [0.9.1] - 2020-05-19 | ||||
| #### Fixed | ||||
| - Fix WordPress compatibility version number | ||||
|  | ||||
| ### [0.9.0] - 2020-05-19 | ||||
| #### Added | ||||
| - Improve explanation of group name filter | ||||
| #### Changed | ||||
| - Update dependencies | ||||
| - Confirm compatibility with WordPress 6.0 | ||||
| #### Fixed | ||||
| - Fix displaying error message for the case the group is not found | ||||
|  | ||||
| ### [0.8.0] - 2022-01-09 | ||||
| #### Added | ||||
| - Add support for older browsers using babel | ||||
|   | ||||
| @@ -15,6 +15,7 @@ require_once __DIR__ . '/includes/settings.php'; | ||||
| require_once __DIR__ . '/includes/date-time-wrapper.php'; | ||||
| require_once __DIR__ . '/includes/formatter.php'; | ||||
| require_once __DIR__ . '/includes/graphql-client.php'; | ||||
| require_once __DIR__ . '/includes/events-list-block.php'; | ||||
| require_once __DIR__ . '/includes/events-list-shortcut.php'; | ||||
| require_once __DIR__ . '/includes/events-list-widget.php'; | ||||
|  | ||||
| @@ -26,7 +27,8 @@ if (!defined('ABSPATH')) { | ||||
| final class Mobilizon_Connector { | ||||
|  | ||||
|   private function __construct() { | ||||
|     add_action('init', [$this, 'register_settings']); | ||||
|     add_action('init', [$this, 'register_blocks']); | ||||
|     add_action('init', [$this, 'register_settings'], 1); // required for register_blocks | ||||
|     add_action('init', [$this, 'register_shortcut']); | ||||
|     add_action('widgets_init', [$this, 'register_widget']); | ||||
|     add_action('wp_enqueue_scripts', [$this, 'register_scripts']); | ||||
| @@ -46,12 +48,29 @@ final class Mobilizon_Connector { | ||||
|     MobilizonConnector\Settings::setDefaultOptions(); | ||||
|   } | ||||
|  | ||||
|   private function load_settings_globally_before_script($scriptName) { | ||||
|     $settings = array( | ||||
|       'isShortOffsetNameShown' => MobilizonConnector\Settings::isShortOffsetNameShown(), | ||||
|       'locale' => str_replace('_', '-', get_locale()), | ||||
|       'timeZone' => wp_timezone_string(), | ||||
|       'url' => MobilizonConnector\Settings::getUrl() | ||||
|     ); | ||||
|     wp_add_inline_script($scriptName, 'var MOBILIZON_CONNECTOR = ' . json_encode($settings), 'before'); | ||||
|   } | ||||
|  | ||||
|   public function register_blocks() { | ||||
|     $scriptName = MobilizonConnector\EventsListBlock::initAndReturnScriptName(); | ||||
|     $this->load_settings_globally_before_script($scriptName); | ||||
|   } | ||||
|  | ||||
|   public function register_settings() { | ||||
|     MobilizonConnector\Settings::init(); | ||||
|   } | ||||
|  | ||||
|   public function register_scripts() { | ||||
|     wp_enqueue_script(MobilizonConnector\NAME . '-js', plugins_url('front/events-loader.js', __FILE__ )); | ||||
|     $name = MobilizonConnector\NAME . '-js'; | ||||
|     wp_enqueue_script($name, plugins_url('front/events-loader.js', __FILE__ )); | ||||
|     $this->load_settings_globally_before_script($name); | ||||
|   } | ||||
|  | ||||
|   public function register_shortcut() { | ||||
|   | ||||
							
								
								
									
										1
									
								
								source/front/block-events-loader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								source/front/block-events-loader.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| import './blocks/events-list/index.js' | ||||
							
								
								
									
										87
									
								
								source/front/blocks/events-list/edit.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								source/front/blocks/events-list/edit.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| /* eslint-disable @wordpress/i18n-ellipsis */ | ||||
| import { loadEventList } from '../../events-loader.js' | ||||
|  | ||||
| const { InspectorControls, useBlockProps } = wp.blockEditor | ||||
| const { PanelBody } = wp.components | ||||
| const { useEffect } = wp.element | ||||
| const { __ } = wp.i18n | ||||
|  | ||||
| const NAME = '<wordpress-name>' | ||||
|  | ||||
| let timer | ||||
|  | ||||
| export default ({ attributes, setAttributes }) => { | ||||
|   const blockProps = useBlockProps({ | ||||
|     className: NAME + '_events-list', | ||||
|     'data-maximum': attributes.eventsCount, | ||||
|     'data-group-name': attributes.groupName, | ||||
|   }) | ||||
|   function reloadEventList() { | ||||
|     if (timer) { | ||||
|       clearTimeout(timer) | ||||
|     } | ||||
|     timer = setTimeout(() => { | ||||
|       const container = document.getElementById(blockProps.id) | ||||
|       if (container) { | ||||
|         loadEventList(container) | ||||
|       } | ||||
|     }, 500) | ||||
|   } | ||||
|   useEffect(() => { | ||||
|     reloadEventList() | ||||
|   }, []) | ||||
|   function updateEventsCount(event) { | ||||
|     let newValue = Number(event.target.value) | ||||
|     if (newValue < 1) newValue = 1 | ||||
|     setAttributes({ eventsCount: newValue }) | ||||
|     reloadEventList() | ||||
|   } | ||||
|   function updateGroupName(event) { | ||||
|     setAttributes({ groupName: event.target.value }) | ||||
|     reloadEventList() | ||||
|   } | ||||
|   return [ | ||||
|     <InspectorControls> | ||||
|       <PanelBody title={__('Events List Settings', '<wordpress-name>')}> | ||||
|         <label | ||||
|           className="components-base-control__label" | ||||
|           htmlFor={NAME + '_events-count'} | ||||
|         > | ||||
|           {__('Number of events to show', '<wordpress-name>')} | ||||
|         </label> | ||||
|         <input | ||||
|           className="components-text-control__input" | ||||
|           type="number" | ||||
|           value={attributes.eventsCount} | ||||
|           onChange={updateEventsCount} | ||||
|           id={NAME + '_events-count'} | ||||
|         /> | ||||
|         <label | ||||
|           className="components-base-control__label" | ||||
|           htmlFor={NAME + '_group-name'} | ||||
|         > | ||||
|           {__('Group name (optional)', '<wordpress-name>')} | ||||
|         </label> | ||||
|         <input | ||||
|           className="components-text-control__input" | ||||
|           type="text" | ||||
|           value={attributes.groupName} | ||||
|           onChange={updateGroupName} | ||||
|           id={NAME + '_group-name'} | ||||
|         /> | ||||
|       </PanelBody> | ||||
|     </InspectorControls>, | ||||
|     <div {...blockProps}> | ||||
|       <div className="general-error" style={{ display: 'none' }}> | ||||
|         {__('The events could not be loaded!', '<wordpress-name>')} | ||||
|       </div> | ||||
|       <div className="group-not-found" style={{ display: 'none' }}> | ||||
|         {__('The group could not be found!', '<wordpress-name>')} | ||||
|       </div> | ||||
|       <div className="loading-indicator" style={{ display: 'none' }}> | ||||
|         {__('Loading...', '<wordpress-name>')} | ||||
|       </div> | ||||
|       <ul style={{ 'list-style-type': 'none', 'padding-left': 0 }}></ul> | ||||
|     </div>, | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										7
									
								
								source/front/blocks/events-list/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								source/front/blocks/events-list/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import edit from './edit.js' | ||||
|  | ||||
| const { registerBlockType } = wp.blocks | ||||
|  | ||||
| const NAME = '<wordpress-name>' | ||||
|  | ||||
| registerBlockType(NAME + '/events-list', { edit }) | ||||
| @@ -1,29 +1,45 @@ | ||||
| import test from 'ava' | ||||
| import { JSDOM } from 'jsdom' | ||||
| import browserEnv from 'browser-env' | ||||
|  | ||||
| import { displayEvents, displayErrorMessage } from './events-displayer.js' | ||||
|  | ||||
| let document | ||||
| import { | ||||
|   displayEvents, | ||||
|   displayErrorMessage, | ||||
|   hideErrorMessages, | ||||
|   showLoadingIndicator, | ||||
| } from './events-displayer.js' | ||||
|  | ||||
| test.before(() => { | ||||
|   document = new JSDOM().window.document | ||||
|   browserEnv() | ||||
|   window.MOBILIZON_CONNECTOR = { | ||||
|     locale: 'en-GB', | ||||
|     timeZone: 'utc', | ||||
|   } | ||||
| }) | ||||
|  | ||||
| test.beforeEach((t) => { | ||||
|   t.context.list = document.createElement('ul') | ||||
|   t.context.list.setAttribute('data-locale', 'en-GB') | ||||
|   t.context.list.setAttribute('data-maximum', '2') | ||||
|   t.context.list.setAttribute('data-time-zone', 'utc') | ||||
|   const listElement = document.createElement('li') | ||||
|   listElement.setAttribute('style', 'display: none;') | ||||
|   t.context.list.appendChild(listElement) | ||||
|   const listElement2 = document.createElement('li') | ||||
|   listElement2.setAttribute('style', 'display: none;') | ||||
|   t.context.list.appendChild(listElement2) | ||||
|   t.context.container = document.createElement('div') | ||||
|   t.context.container.setAttribute('data-maximum', '2') | ||||
|  | ||||
|   const errorMessageGeneral = document.createElement('div') | ||||
|   errorMessageGeneral.setAttribute('class', 'general-error') | ||||
|   errorMessageGeneral.setAttribute('style', 'display: none;') | ||||
|   t.context.container.appendChild(errorMessageGeneral) | ||||
|  | ||||
|   const errorMessageGroupNotFound = document.createElement('div') | ||||
|   errorMessageGroupNotFound.setAttribute('class', 'group-not-found') | ||||
|   errorMessageGroupNotFound.setAttribute('style', 'display: none;') | ||||
|   t.context.container.appendChild(errorMessageGroupNotFound) | ||||
|  | ||||
|   const loadingIndicator = document.createElement('div') | ||||
|   loadingIndicator.setAttribute('class', 'loading-indicator') | ||||
|   loadingIndicator.setAttribute('style', 'display: none;') | ||||
|   t.context.container.appendChild(loadingIndicator) | ||||
|  | ||||
|   const list = document.createElement('ul') | ||||
|   t.context.container.appendChild(list) | ||||
| }) | ||||
|  | ||||
| test('#displayEvents one event', (t) => { | ||||
|   const list = t.context.list | ||||
|   const data = { | ||||
|     events: { | ||||
|       elements: [ | ||||
| @@ -40,32 +56,35 @@ test('#displayEvents one event', (t) => { | ||||
|       ], | ||||
|     }, | ||||
|   } | ||||
|   displayEvents({ data, document, list }) | ||||
|   t.is(list.children.length, 3) | ||||
|   t.is(list.children[2].childNodes[0].tagName, 'A') | ||||
|   t.is(list.children[2].childNodes[0].getAttribute('href'), 'b') | ||||
|   t.is(list.children[2].childNodes[0].childNodes[0].nodeValue, 'a') | ||||
|   t.is(list.children[2].childNodes[1].tagName, 'BR') | ||||
|   t.is(list.children[2].childNodes[2].nodeValue, '15/04/2021 10:30 - 15:30') | ||||
|   t.is(list.children[2].childNodes[3].tagName, 'BR') | ||||
|   t.is(list.children[2].childNodes[4].nodeValue, 'c, d') | ||||
|   const container = t.context.container | ||||
|   displayEvents({ data, document, container }) | ||||
|   const list = container.querySelector('ul') | ||||
|   t.is(list.children[0].childNodes[0].tagName, 'A') | ||||
|   t.is(list.children[0].childNodes[0].getAttribute('href'), 'b') | ||||
|   t.is(list.children[0].childNodes[0].childNodes[0].nodeValue, 'a') | ||||
|   t.is(list.children[0].childNodes[1].tagName, 'BR') | ||||
|   t.is(list.children[0].childNodes[2].nodeValue, '15/04/2021 10:30 - 15:30') | ||||
|   t.is(list.children[0].childNodes[3].tagName, 'BR') | ||||
|   t.is(list.children[0].childNodes[4].nodeValue, 'c, d') | ||||
| }) | ||||
|  | ||||
| test('#displayErrorMessage no children added', (t) => { | ||||
|   const list = t.context.list | ||||
|   displayErrorMessage({ data: '', list }) | ||||
|   t.is(list.children.length, 2) | ||||
| test('#displayErrorMessage no list entries shown', (t) => { | ||||
|   const container = t.context.container | ||||
|   displayErrorMessage({ data: '', container }) | ||||
|   const list = container.querySelector('ul') | ||||
|   t.is(list.children.length, 0) | ||||
| }) | ||||
|  | ||||
| test('#displayErrorMessage error message display', (t) => { | ||||
|   const list = t.context.list | ||||
|   displayErrorMessage({ data: '', list }) | ||||
|   t.is(list.children[0].style.display, 'block') | ||||
|   t.is(list.children[1].style.display, 'none') | ||||
| test('#displayErrorMessage general error message display', (t) => { | ||||
|   const container = t.context.container | ||||
|   displayErrorMessage({ data: '', container }) | ||||
|   t.is(container.querySelector('.general-error').style.display, 'block') | ||||
|   t.is(container.querySelector('.group-not-found').style.display, 'none') | ||||
|   t.is(container.querySelector('.loading-indicator').style.display, 'none') | ||||
| }) | ||||
|  | ||||
| test('#displayErrorMessage group not found error message display', (t) => { | ||||
|   const list = t.context.list | ||||
|   const container = t.context.container | ||||
|   const data = { | ||||
|     response: { | ||||
|       errors: [ | ||||
| @@ -75,7 +94,29 @@ test('#displayErrorMessage group not found error message display', (t) => { | ||||
|       ], | ||||
|     }, | ||||
|   } | ||||
|   displayErrorMessage({ data, list }) | ||||
|   t.is(list.children[0].style.display, 'none') | ||||
|   t.is(list.children[1].style.display, 'block') | ||||
|   displayErrorMessage({ data, container }) | ||||
|   t.is(container.querySelector('.general-error').style.display, 'none') | ||||
|   t.is(container.querySelector('.group-not-found').style.display, 'block') | ||||
|   t.is(container.querySelector('.loading-indicator').style.display, 'none') | ||||
| }) | ||||
|  | ||||
| test('#showLoadingIndicator remove events', (t) => { | ||||
|   const container = t.context.container | ||||
|   const loadingIndicator = container.querySelector('.loading-indicator') | ||||
|   t.is(loadingIndicator.style.display, 'none') | ||||
|   showLoadingIndicator(container) | ||||
|   t.is(loadingIndicator.style.display, 'block') | ||||
| }) | ||||
|  | ||||
| test('#hideErrorMessages remove events', (t) => { | ||||
|   const container = t.context.container | ||||
|   const generalErrorMessage = container.querySelector('.general-error') | ||||
|   const groupNotFoundErrorMessage = container.querySelector('.group-not-found') | ||||
|   generalErrorMessage.style.display = 'block' | ||||
|   groupNotFoundErrorMessage.style.display = 'block' | ||||
|   t.is(generalErrorMessage.style.display, 'block') | ||||
|   t.is(groupNotFoundErrorMessage.style.display, 'block') | ||||
|   hideErrorMessages(container) | ||||
|   t.is(generalErrorMessage.style.display, 'none') | ||||
|   t.is(groupNotFoundErrorMessage.style.display, 'none') | ||||
| }) | ||||
|   | ||||
| @@ -1,17 +1,25 @@ | ||||
| import Formatter from './formatter.js' | ||||
| import { createAnchorElement } from './html-creator.js' | ||||
|  | ||||
| export function displayEvents({ data, document, list }) { | ||||
|   const locale = list.getAttribute('data-locale') | ||||
|   const maxEventsCount = list.getAttribute('data-maximum') | ||||
|   const timeZone = list.getAttribute('data-time-zone') | ||||
|   const isShortOffsetNameShown = list.hasAttribute( | ||||
|     'data-is-short-offset-name-shown' | ||||
|   ) | ||||
| export function clearEventsList(container) { | ||||
|   const list = container.querySelector('ul') | ||||
|   list.replaceChildren() | ||||
| } | ||||
|  | ||||
| export function displayEvents({ data, document, container }) { | ||||
|   hideLoadingIndicator(container) | ||||
|  | ||||
|   const isShortOffsetNameShown = | ||||
|     window.MOBILIZON_CONNECTOR.isShortOffsetNameShown | ||||
|   const locale = window.MOBILIZON_CONNECTOR.locale | ||||
|   const maxEventsCount = container.getAttribute('data-maximum') | ||||
|   const timeZone = window.MOBILIZON_CONNECTOR.timeZone | ||||
|  | ||||
|   const events = data.events | ||||
|     ? data.events.elements | ||||
|     : data.group.organizedEvents.elements | ||||
|   const eventsCount = Math.min(maxEventsCount, events.length) | ||||
|   const list = container.querySelector('ul') | ||||
|   for (let i = 0; i < eventsCount; i++) { | ||||
|     const li = document.createElement('li') | ||||
|  | ||||
| @@ -53,8 +61,8 @@ export function displayEvents({ data, document, list }) { | ||||
|   } | ||||
| } | ||||
|  | ||||
| export function displayErrorMessage({ data, list }) { | ||||
|   console.error(data) | ||||
| export function displayErrorMessage({ data, container }) { | ||||
|   hideLoadingIndicator(container) | ||||
|   if ( | ||||
|     Object.prototype.hasOwnProperty.call(data, 'response') && | ||||
|     Object.prototype.hasOwnProperty.call(data.response, 'errors') && | ||||
| @@ -62,8 +70,26 @@ export function displayErrorMessage({ data, list }) { | ||||
|     Object.prototype.hasOwnProperty.call(data.response.errors[0], 'code') && | ||||
|     data.response.errors[0].code === 'group_not_found' | ||||
|   ) { | ||||
|     list.children[1].style.display = 'block' | ||||
|     const message = container.querySelector('.group-not-found') | ||||
|     message.style.display = 'block' | ||||
|   } else { | ||||
|     list.children[0].style.display = 'block' | ||||
|     const message = container.querySelector('.general-error') | ||||
|     message.style.display = 'block' | ||||
|     console.error(data) | ||||
|   } | ||||
| } | ||||
|  | ||||
| export function showLoadingIndicator(container) { | ||||
|   const indicator = container.querySelector('.loading-indicator') | ||||
|   indicator.style.display = 'block' | ||||
| } | ||||
|  | ||||
| function hideLoadingIndicator(container) { | ||||
|   const indicator = container.querySelector('.loading-indicator') | ||||
|   indicator.style.display = 'none' | ||||
| } | ||||
|  | ||||
| export function hideErrorMessages(container) { | ||||
|   container.querySelector('.group-not-found').style.display = 'none' | ||||
|   container.querySelector('.general-error').style.display = 'none' | ||||
| } | ||||
|   | ||||
| @@ -1,22 +1,38 @@ | ||||
| import { displayEvents, displayErrorMessage } from './events-displayer.js' | ||||
| import { | ||||
|   clearEventsList, | ||||
|   displayEvents, | ||||
|   displayErrorMessage, | ||||
|   hideErrorMessages, | ||||
|   showLoadingIndicator, | ||||
| } from './events-displayer.js' | ||||
| import * as GraphqlWrapper from './graphql-wrapper.js' | ||||
|  | ||||
| const NAME = '<wordpress-name>' | ||||
| const URL_SUFFIX = '/api' | ||||
|  | ||||
| document.addEventListener('DOMContentLoaded', () => { | ||||
| document.addEventListener('DOMContentLoaded', loadEventLists) | ||||
|  | ||||
| function loadEventLists() { | ||||
|   const eventLists = document.getElementsByClassName(NAME + '_events-list') | ||||
|   for (const list of eventLists) { | ||||
|     const url = list.getAttribute('data-url') + '/api' | ||||
|     const limit = parseInt(list.getAttribute('data-maximum')) | ||||
|     const groupName = list.getAttribute('data-group-name') | ||||
|     if (groupName) { | ||||
|       GraphqlWrapper.getUpcomingEventsByGroupName({ url, limit, groupName }) | ||||
|         .then((data) => displayEvents({ data, document, list })) | ||||
|         .catch((data) => displayErrorMessage({ data, list })) | ||||
|     } else { | ||||
|       GraphqlWrapper.getUpcomingEvents({ url, limit }) | ||||
|         .then((data) => displayEvents({ data, document, list })) | ||||
|         .catch((data) => displayErrorMessage({ data, list })) | ||||
|     } | ||||
|     loadEventList(list) | ||||
|   } | ||||
| }) | ||||
| } | ||||
|  | ||||
| export function loadEventList(container) { | ||||
|   const url = MOBILIZON_CONNECTOR.url + URL_SUFFIX | ||||
|   const limit = parseInt(container.getAttribute('data-maximum')) | ||||
|   const groupName = container.getAttribute('data-group-name') | ||||
|   hideErrorMessages(container) | ||||
|   clearEventsList(container) | ||||
|   showLoadingIndicator(container) | ||||
|   if (groupName) { | ||||
|     GraphqlWrapper.getUpcomingEventsByGroupName({ url, limit, groupName }) | ||||
|       .then((data) => displayEvents({ data, document, container })) | ||||
|       .catch((data) => displayErrorMessage({ data, container })) | ||||
|   } else { | ||||
|     GraphqlWrapper.getUpcomingEvents({ url, limit }) | ||||
|       .then((data) => displayEvents({ data, document, container })) | ||||
|       .catch((data) => displayErrorMessage({ data, container })) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -37,7 +37,7 @@ export function getUpcomingEvents({ url, limit }) { | ||||
|  | ||||
| export function getUpcomingEventsByGroupName({ url, limit, groupName }) { | ||||
|   const query = ` | ||||
|     query ($afterDatetime: DateTime, $groupName: String, $limit: Int) { | ||||
|     query ($afterDatetime: DateTime, $groupName: String!, $limit: Int) { | ||||
|       group(preferredUsername: $groupName) { | ||||
|         organizedEvents(afterDatetime: $afterDatetime, limit: $limit) { | ||||
|           elements { | ||||
|   | ||||
| @@ -1,12 +1,10 @@ | ||||
| import test from 'ava' | ||||
| import { JSDOM } from 'jsdom' | ||||
| import browserEnv from 'browser-env' | ||||
|  | ||||
| import { createAnchorElement } from './html-creator.js' | ||||
|  | ||||
| let document | ||||
|  | ||||
| test.beforeEach(() => { | ||||
|   document = new JSDOM().window.document | ||||
|   browserEnv() | ||||
| }) | ||||
|  | ||||
| test('#createAnchorElement usual parameters', (t) => { | ||||
|   | ||||
							
								
								
									
										53
									
								
								source/includes/events-list-block.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								source/includes/events-list-block.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| <?php | ||||
| namespace MobilizonConnector; | ||||
|  | ||||
| // Exit if this file is called directly. | ||||
| if (!defined('ABSPATH')) { | ||||
|   exit; | ||||
| } | ||||
|  | ||||
| class EventsListBlock { | ||||
|  | ||||
|   public static function initAndReturnScriptName(): string { | ||||
|     $scriptName = NAME . '-block-starter'; | ||||
|     wp_register_script($scriptName, plugins_url(NAME . '/front/block-events-loader.js'), [ | ||||
|         'wp-block-editor', | ||||
|         'wp-blocks', | ||||
|         'wp-components', | ||||
|         'wp-i18n' | ||||
|       ]); | ||||
|     register_block_type(NAME . '/events-list', [ | ||||
|       'api_version' => 2, | ||||
|       'title' => __('Events List', 'connector-mobilizon'), | ||||
|       'description' =>  __('A list of the upcoming events of the connected Mobilizon instance.', 'connector-mobilizon'), | ||||
|       'category' => 'widgets', | ||||
|       'icon' => 'list-view', | ||||
|       'supports' => [ | ||||
|         'html' => false | ||||
|       ], | ||||
|       'attributes' => [ | ||||
|         'eventsCount' => [ | ||||
|           'type' => 'number', | ||||
|           'default' => 3, | ||||
|         ], | ||||
|         'groupName' => [ | ||||
|           'type' => 'string', | ||||
|         ], | ||||
|       ], | ||||
|       'editor_script' => $scriptName, | ||||
|       'render_callback' => 'MobilizonConnector\EventsListBlock::render', | ||||
|     ]); | ||||
|     return $scriptName; | ||||
|   } | ||||
|  | ||||
|   public static function render($block_attributes, $content) { | ||||
|     $classNamePrefix = NAME; | ||||
|     $eventsCount = $block_attributes['eventsCount']; | ||||
|     $groupName = isset($block_attributes['groupName']) ? $block_attributes['groupName'] : ''; | ||||
|  | ||||
|     ob_start(); | ||||
|     require dirname(__DIR__) . '/view/events-list.php'; | ||||
|     $output = ob_get_clean(); | ||||
|     return $output; | ||||
|   } | ||||
| } | ||||
| @@ -26,11 +26,7 @@ class EventsListShortcut { | ||||
|  | ||||
|     $classNamePrefix = NAME; | ||||
|     $eventsCount = $atts_with_overriden_defaults['events-count']; | ||||
|     $locale = str_replace('_', '-', get_locale()); | ||||
|     $groupName = $atts_with_overriden_defaults['group-name']; | ||||
|     $url = Settings::getUrl(); | ||||
|     $timeZone = wp_timezone_string(); | ||||
|     $isShortOffsetNameShown = Settings::isShortOffsetNameShown(); | ||||
|  | ||||
|     ob_start(); | ||||
|     require dirname(__DIR__) . '/view/events-list.php'; | ||||
|   | ||||
| @@ -29,9 +29,6 @@ class EventsListWidget extends \WP_Widget { | ||||
|     $eventsCount = $options['eventsCount']; | ||||
|     $locale = str_replace('_', '-', get_locale()); // TODO _ is okay too. | ||||
|     $groupName = isset($options['groupName']) ? $options['groupName'] : ''; | ||||
|     $url = Settings::getUrl(); | ||||
|     $timeZone = wp_timezone_string(); | ||||
|     $isShortOffsetNameShown = Settings::isShortOffsetNameShown(); | ||||
|  | ||||
|     if ($groupName) { | ||||
|       $data = GraphQlClient::get_upcoming_events_by_group_name($url, (int) $eventsCount, $groupName); // TODO wrap and put into shortcut as well | ||||
|   | ||||
| @@ -15,27 +15,70 @@ License: <wordpress-license> | ||||
| <wordpress-nice-name> allows you to display the upcoming events of [Mobilizon](https://joinmobilizon.org/), which is a federated event listing platform, on your WordPress website. | ||||
|  | ||||
| Features | ||||
| - Display events as widget and as shortcut | ||||
| - Display events' title, date, and location if available | ||||
| - Display events as Gutenberg block, as widget and as shortcut | ||||
| - Display events' title, date, and location, if available | ||||
| - Cache requests' responses for 2 minutes in the browser's `sessionStorage` | ||||
| - Configure number of events to show per widget and per shortcut | ||||
| - Optionally filter events by a specific group per widget and per shortcut | ||||
| - Configure number of events to show per block, per widget and per shortcut | ||||
| - Optionally filter events by a specific group per block, per widget and per shortcut | ||||
| - Set the URL of the Mobilizon instance in the settings | ||||
| - Toggle adding named offset in brackets after the time in the settings | ||||
|  | ||||
| Shortcut format with limiting the number of events to show to 3 for example: `[<wordpress-name>-events-list events-count=3]` | ||||
| Optionally, you can only show the events of a specific group by indicatings its name: `[<wordpress-name>-events-list events-count=3 group-name="mygroup"]` | ||||
|  | ||||
| The source code is available on [Github](https://github.com/dwaxweiler/connector-mobilizon). | ||||
|  | ||||
| ## Installation | ||||
|  | ||||
| Shortcut format with limiting the number of events to show to 3 for example: `[<wordpress-name>-events-list events-count=3]` | ||||
|  | ||||
| Optionally, you can only show the events of a specific group by indicatings its name: `[<wordpress-name>-events-list events-count=3 group-name="mygroup"]` | ||||
| You have to use their username, e.g. `@nosliensvivants`, and append the name of their instance if they use a different one, e.g. `@yaam_berlin@mobilize.berlin`. | ||||
|  | ||||
| ## Screenshots | ||||
| 1. Events list | ||||
| 2. General settings | ||||
| 3. Widget creation | ||||
| 4. Shortcut creation | ||||
| 5. Gutenberg block in editor | ||||
|  | ||||
| ## Changelog | ||||
|  | ||||
| ### [0.11.1] | ||||
| #### Fixed | ||||
| - Revert minimum PHP version to 7.4 to allow some more time for upgrading PHP | ||||
|  | ||||
| ### [0.11.0] | ||||
| #### Changed | ||||
| - Update dependencies | ||||
| - Confirm compatibility with WordPress 6.2 | ||||
| #### Security | ||||
| - Set minimum PHP version to oldest stable 8.0 | ||||
|  | ||||
| ### [0.10.1] | ||||
| #### Changed | ||||
| - Confirm compatibility with WordPress 6.1 | ||||
| - Update dependencies | ||||
|  | ||||
| ### [0.10.0] | ||||
| #### Added | ||||
| - Add Gutenberg events list block | ||||
| - Show loading indicator during request | ||||
| #### Changed | ||||
| - Set list style type to none and left padding to zero for all occurences | ||||
| - Move shortcut usage description into installation section in `readme.txt` | ||||
| - Update dependencies | ||||
|  | ||||
| ### [0.9.1] - 2020-05-19 | ||||
| #### Fixed | ||||
| - Fix WordPress compatibility version number | ||||
|  | ||||
| ### [0.9.0] - 2020-05-19 | ||||
| #### Added | ||||
| - Improve explanation of group name filter | ||||
| #### Changed | ||||
| - Update dependencies | ||||
| - Confirm compatibility with WordPress 6.0 | ||||
| #### Fixed | ||||
| - Fix displaying error message for the case the group is not found | ||||
|  | ||||
| ### [0.8.0] - 2022-01-09 | ||||
| #### Added | ||||
| - Add support for older browsers using babel | ||||
|   | ||||
| @@ -4,9 +4,7 @@ if (!defined('ABSPATH')) { | ||||
|   exit; | ||||
| } | ||||
| ?> | ||||
| <ul class="<?php echo esc_attr($classNamePrefix); ?>_events-list" | ||||
|   data-url="<?php echo esc_attr($url); ?>" | ||||
|   data-locale="<?php echo esc_attr($locale); ?>" | ||||
| <div class="<?php echo esc_attr($classNamePrefix); ?>_events-list" | ||||
|   data-maximum="<?php echo esc_attr($eventsCount); ?>" | ||||
|   data-group-name="<?php echo esc_attr($groupName); ?>" | ||||
|   data-time-zone="<?php echo esc_attr($timeZone); ?>" | ||||
|   | ||||
| @@ -6,10 +6,13 @@ const CopyPlugin = require('copy-webpack-plugin') | ||||
| const FOLDER_SOURCE = './source' | ||||
|  | ||||
| module.exports = { | ||||
|   entry: FOLDER_SOURCE + '/front/events-loader.js', | ||||
|   entry: { | ||||
|     'block-events-loader': FOLDER_SOURCE + '/front/block-events-loader.js', | ||||
|     'events-loader': FOLDER_SOURCE + '/front/events-loader.js', | ||||
|   }, | ||||
|   output: { | ||||
|     filename: 'events-loader.js', | ||||
|     path: path.resolve(__dirname, 'build/' + '/front'), | ||||
|     filename: '[name].js', | ||||
|     path: path.resolve(__dirname, 'build/front'), | ||||
|   }, | ||||
|   module: { | ||||
|     rules: [ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user