1
0
mirror of https://github.com/dwaxweiler/connector-mobilizon synced 2025-06-05 21:59:25 +02:00

33 Commits

Author SHA1 Message Date
44f35aa007 release version 1.4.0 2025-05-22 21:37:51 +02:00
f3b7dcf735 confirm compatibility with WordPress 6.8 2025-05-22 21:27:35 +02:00
e3f2e5b133 replace outdated browser-env with jsdom 2025-05-22 21:26:37 +02:00
09c0698509 update dependencies 2025-05-22 21:06:03 +02:00
f69769d291 prepare next release 2024-11-10 09:53:15 +01:00
e21c86f2e4 release version 1.3.0 2024-11-10 09:52:09 +01:00
5bcc03f9d1 handle location being null 2024-11-10 09:46:26 +01:00
460ea7894d update dependencies 2024-11-10 09:38:57 +01:00
8caceeaf76 add missing changelog entries 2024-11-10 09:12:09 +01:00
0c40efd565 load block script only in footer 2024-11-09 22:32:53 +01:00
e91ca97e88 add version to wp_register_script() 2024-11-09 11:20:34 +01:00
df2feceaaa use escape-only function for event-related data 2024-11-09 10:01:50 +01:00
bcd1f5247d confirm compatibility with WordPress 6.7 2024-11-09 09:54:47 +01:00
e2d34032cd add comment for translators 2024-11-08 23:17:19 +01:00
9708203f3c prepare next release 2024-08-04 19:01:13 +02:00
370e0d9e46 release version 1.2.0 2024-08-04 19:00:14 +02:00
9412b9cb90 add missing changelog entry 2024-08-04 18:54:48 +02:00
72045a31b0 update dependencies 2024-08-04 18:54:13 +02:00
0edad986d3 display event picture (#23) 2024-08-04 18:31:29 +02:00
a543a25a8a prepare next release 2024-07-18 19:20:22 +02:00
f0e955aa47 release version 1.1.0 2024-07-18 19:19:17 +02:00
80abd9a461 npm audit fix 2024-07-18 19:04:55 +02:00
fb1db8e836 update dependencies 2024-07-18 19:03:41 +02:00
db080657db confirm compatibility with WordPress 6.6 2024-07-18 18:51:47 +02:00
979ecbc91f upgrade gulp to version 5 2024-05-16 08:57:35 +01:00
08e80615c6 add note about API to description 2024-05-15 08:34:06 +01:00
526d57d1b2 add missing changelog entry 2024-05-15 08:32:32 +01:00
718d66506b update some dev dependencies 2024-05-15 08:31:06 +01:00
cccd1a78b5 add some spacing between event items 2024-05-14 10:30:42 +02:00
fa99821ffc fix undefined variable for both error views 2024-05-14 10:28:39 +02:00
7bc35a3923 mention use of GraphQL API 2024-05-13 14:23:37 +02:00
82800a9db3 clarify two steps in release procedure 2024-04-06 08:27:24 +02:00
46170377af prepare next release 2024-04-06 08:25:16 +02:00
18 changed files with 6795 additions and 10862 deletions

View File

@ -1,2 +1,2 @@
npm test npm test
npx lint-staged lint-staged

View File

@ -6,6 +6,8 @@ More details can be found in the [WordPress Plugin Directory](https://wordpress.
The current changelog can be found under [source/changelog.txt](source/changelog.txt). The current changelog can be found under [source/changelog.txt](source/changelog.txt).
This plugin uses [Mobilizon's GraphQL API](https://docs.joinmobilizon.org/contribute/graphql_api/).
## Development ## Development
### Setup ### Setup
@ -22,21 +24,23 @@ The current changelog can be found under [source/changelog.txt](source/changelog
### Release procedure ### Release procedure
1. Make sure `changelog.txt` is up-to-date. 1. Make sure `changelog.txt` is up-to-date.
2. Use a new version number and copy over the new section into `readme.txt`. 2. Create a new section with a new version number.
3. Update `package.json` with the same version number. 3. Copy over the new section into `readme.txt`.
4. Update the `package-lock.json`: `npm i --package-lock-only` 4. Update `package.json` with the same version number.
5. Build: `npm run build-prod` 5. Update the `package-lock.json`: `npm i --package-lock-only`
6. Make sure screenshots are up-to-date. 6. Build: `npm run build-prod`
7. Copy the built plugin into `/trunk` of SVN. 7. Make sure screenshots are up-to-date.
8. Create a new tag of the new version: `svn cp trunk tags/<version>` 8. Copy the built plugin into `/trunk` of SVN.
9. Check the version number occurrences in both folders. 9. Create a new tag of the new version: `svn cp trunk tags/<version>`
10. Commit everything together to the release SVN: `svn ci -m "release version <version>"` Make sure to add new files beforehand. 10. Check the version number occurrences in both folders.
11. Commit the new version in git with the same message. 11. Make sure to handle exclamation and question marks in `svn status`.
12. Tag the new version: `git tag v<version>` 12. Commit everything together to the release SVN: `svn ci -m "release version <version>"`
13. Push the new tag to the repository: `git push --tags` 13. Commit the new version in git with the same message.
14. Append `-next` to the version number in `package.json`. 14. Tag the new version: `git tag v<version>`
15. Update the `package-lock.json`: `npm i --package-lock-only` 15. Push the new tag to the repository: `git push --tags`
16. Commit: `git commit -am "prepare next release"` 16. Append `-next` to the version number in `package.json`.
17. Update the `package-lock.json`: `npm i --package-lock-only`
18. Commit: `git commit -am "prepare next release"`
### Other commands ### Other commands

17387
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "connector-mobilizon", "name": "connector-mobilizon",
"version": "1.0.0", "version": "1.4.0",
"description": "Display Mobilizon events in WordPress.", "description": "Display Mobilizon events in WordPress.",
"private": true, "private": true,
"type": "module", "type": "module",
@ -24,33 +24,33 @@
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"graphql": "16.8.1", "graphql": "16.11.0",
"luxon": "3.4.4" "luxon": "3.6.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.24.4", "@babel/core": "7.27.1",
"@babel/eslint-parser": "7.24.1", "@babel/eslint-parser": "7.27.1",
"@babel/preset-env": "7.24.4", "@babel/preset-env": "7.27.2",
"@babel/preset-react": "7.24.1", "@babel/preset-react": "7.27.1",
"@wordpress/eslint-plugin": "17.12.0", "@wordpress/eslint-plugin": "22.10.0",
"ava": "6.1.2", "ava": "6.3.0",
"babel-loader": "9.1.3", "babel-loader": "10.0.0",
"browser-env": "3.3.0", "c8": "10.1.3",
"c8": "9.1.0", "copy-webpack-plugin": "13.0.0",
"copy-webpack-plugin": "12.0.2", "eslint": "8.57.1",
"eslint": "8.57.0",
"eslint-plugin-ava": "14.0.0", "eslint-plugin-ava": "14.0.0",
"eslint-plugin-jsx": "0.1.0", "eslint-plugin-jsx": "0.1.0",
"eslint-plugin-react": "7.34.1", "eslint-plugin-react": "7.37.5",
"esm": "3.2.25", "esm": "3.2.25",
"gulp": "4.0.2", "gulp": "5.0.0",
"gulp-replace": "1.1.4", "gulp-replace": "1.1.4",
"husky": "9.0.11", "husky": "9.1.7",
"lint-staged": "15.2.2", "jsdom": "26.1.0",
"prettier": "3.2.5", "lint-staged": "15.5.2",
"rimraf": "5.0.5", "prettier": "3.5.3",
"webpack": "5.91.0", "rimraf": "5.0.10",
"webpack-cli": "5.1.4" "webpack": "5.99.9",
"webpack-cli": "6.0.1"
}, },
"ava": { "ava": {
"files": [ "files": [
@ -61,7 +61,7 @@
"niceName": "Connector for Mobilizon", "niceName": "Connector for Mobilizon",
"phpMinimumVersion": 7.4, "phpMinimumVersion": 7.4,
"wordpressMinimumVersion": 5.6, "wordpressMinimumVersion": 5.6,
"wordpressTestedUpToVersion": "6.5" "wordpressTestedUpToVersion": "6.8"
}, },
"lint-staged": { "lint-staged": {
"source/**/*.js": "eslint", "source/**/*.js": "eslint",

View File

@ -6,6 +6,38 @@
#### Fixed #### Fixed
#### Security #### Security
### [1.4.0]
#### Changed
- Update dependencies
- Confirm compatibility with WordPress 6.8
### [1.3.0]
#### Added
- Comment for translators what placeholder will contain
#### Changed
- Confirm compatibility with WordPress 6.7
- Load block script only in footer to reduce waiting time
- Update dependencies
#### Fixed
- Mark event-related data as non-translatable within plugin
- Add version number to script registration to break browser caching
- Handle location being null
### [1.2.0]
#### Added
- Display event picture if available
#### Changed
- Update dependencies
### [1.1.0]
#### Added
- Add some spacing between event items
#### Changed
- Update dependencies
- Confirm compatibility with WordPress 6.6
#### Fixed
- Fix undefined variable $classNamePrefix for both error views
### [1.0.0] ### [1.0.0]
#### Added #### Added
- Display name of group when it cannot be found - Display name of group when it cannot be found

View File

@ -1,5 +1,5 @@
import test from 'ava' import test from 'ava'
import browserEnv from 'browser-env' import { JSDOM } from 'jsdom'
import { import {
displayEvents, displayEvents,
@ -9,7 +9,9 @@ import {
} from './events-displayer.js' } from './events-displayer.js'
test.before(() => { test.before(() => {
browserEnv() const dom = new JSDOM()
global.document = dom.window.document
global.window = dom.window
window.MOBILIZON_CONNECTOR = { window.MOBILIZON_CONNECTOR = {
locale: 'en-GB', locale: 'en-GB',
timeZone: 'utc', timeZone: 'utc',

View File

@ -1,5 +1,5 @@
import Formatter from './formatter.js' import Formatter from './formatter.js'
import { createAnchorElement } from './html-creator.js' import { createAnchorElement, createImageElement } from './html-creator.js'
export function clearEventsList(container) { export function clearEventsList(container) {
const list = container.querySelector('ul') const list = container.querySelector('ul')
@ -18,6 +18,19 @@ export function displayEvents({ events, document, container, maxEventsCount }) {
const list = container.querySelector('ul') const list = container.querySelector('ul')
for (let i = 0; i < eventsCount; i++) { for (let i = 0; i < eventsCount; i++) {
const li = document.createElement('li') const li = document.createElement('li')
li.style.lineHeight = '150%'
li.style.marginTop = '20px'
if (events[i].picture) {
const img = createImageElement({
document,
alt: events[i].picture.alt ? events[i].picture.alt : '',
src: events[i].picture.base64 ? events[i].picture.base64 : '',
})
img.style.display = 'block'
img.style.maxWidth = '100%'
li.appendChild(img)
}
const a = createAnchorElement({ const a = createAnchorElement({
document, document,

View File

@ -1,10 +1,11 @@
import test from 'ava' import test from 'ava'
import browserEnv from 'browser-env' import { JSDOM } from 'jsdom'
import { createAnchorElement } from './html-creator.js' import { createAnchorElement } from './html-creator.js'
test.beforeEach(() => { test.beforeEach(() => {
browserEnv() const dom = new JSDOM()
global.document = dom.window.document
}) })
test('#createAnchorElement usual parameters', (t) => { test('#createAnchorElement usual parameters', (t) => {

View File

@ -5,3 +5,10 @@ export function createAnchorElement({ document, text, url }) {
a.innerHTML = text a.innerHTML = text
return a return a
} }
export function createImageElement({ document, alt, src }) {
const img = document.createElement('img')
img.setAttribute('alt', alt)
img.setAttribute('src', src)
return img
}

View File

@ -10,7 +10,7 @@ class EventsListBlock {
'wp-blocks', 'wp-blocks',
'wp-components', 'wp-components',
'wp-i18n' 'wp-i18n'
]); ], '<wordpress-version>', array('in_footer' => true));
register_block_type(NAME . '/events-list', [ register_block_type(NAME . '/events-list', [
'api_version' => 2, 'api_version' => 2,
'title' => __('Events List', 'connector-mobilizon'), 'title' => __('Events List', 'connector-mobilizon'),
@ -39,6 +39,7 @@ class EventsListBlock {
$url = Settings::getUrl(); $url = Settings::getUrl();
$eventsCount = $block_attributes['eventsCount']; $eventsCount = $block_attributes['eventsCount'];
$groupName = isset($block_attributes['groupName']) ? $block_attributes['groupName'] : ''; $groupName = isset($block_attributes['groupName']) ? $block_attributes['groupName'] : '';
$classNamePrefix = NAME;
ob_start(); ob_start();
try { try {
@ -48,7 +49,6 @@ class EventsListBlock {
$events = GraphQlClient::get_upcoming_events($url, (int) $eventsCount); $events = GraphQlClient::get_upcoming_events($url, (int) $eventsCount);
} }
$classNamePrefix = NAME;
$locale = get_locale(); $locale = get_locale();
$isShortOffsetNameShown = Settings::isShortOffsetNameShown(); $isShortOffsetNameShown = Settings::isShortOffsetNameShown();
$timeZone = wp_timezone_string(); $timeZone = wp_timezone_string();

View File

@ -22,6 +22,7 @@ class EventsListShortcut {
$url = Settings::getUrl(); $url = Settings::getUrl();
$eventsCount = $atts_with_overriden_defaults['events-count']; $eventsCount = $atts_with_overriden_defaults['events-count'];
$groupName = $atts_with_overriden_defaults['group-name']; $groupName = $atts_with_overriden_defaults['group-name'];
$classNamePrefix = NAME;
ob_start(); ob_start();
try { try {
@ -31,7 +32,6 @@ class EventsListShortcut {
$events = GraphQlClient::get_upcoming_events($url, (int) $eventsCount); $events = GraphQlClient::get_upcoming_events($url, (int) $eventsCount);
} }
$classNamePrefix = NAME;
$locale = get_locale(); $locale = get_locale();
$isShortOffsetNameShown = Settings::isShortOffsetNameShown(); $isShortOffsetNameShown = Settings::isShortOffsetNameShown();
$timeZone = wp_timezone_string(); $timeZone = wp_timezone_string();

View File

@ -23,6 +23,7 @@ class EventsListWidget extends \WP_Widget {
$url = Settings::getUrl(); $url = Settings::getUrl();
$eventsCount = $options['eventsCount']; $eventsCount = $options['eventsCount'];
$groupName = isset($options['groupName']) ? $options['groupName'] : ''; $groupName = isset($options['groupName']) ? $options['groupName'] : '';
$classNamePrefix = NAME;
try { try {
if ($groupName) { if ($groupName) {
@ -31,7 +32,6 @@ class EventsListWidget extends \WP_Widget {
$events = GraphQlClient::get_upcoming_events($url, (int) $eventsCount); $events = GraphQlClient::get_upcoming_events($url, (int) $eventsCount);
} }
$classNamePrefix = NAME;
$locale = get_locale(); $locale = get_locale();
$isShortOffsetNameShown = Settings::isShortOffsetNameShown(); $isShortOffsetNameShown = Settings::isShortOffsetNameShown();
$timeZone = wp_timezone_string(); $timeZone = wp_timezone_string();

View File

@ -26,7 +26,7 @@ final class Formatter
return $dateText; return $dateText;
} }
public static function format_location(string $description, string $locality): string { public static function format_location(string $description, ?string $locality): string {
$location = ''; $location = '';
if ($description && trim($description)) { if ($description && trim($description)) {
$location .= trim($description); $location .= trim($description);

View File

@ -40,6 +40,11 @@ final class GraphQlClient {
physicalAddress { physicalAddress {
description, description,
locality locality
},
picture {
alt,
contentType,
url
} }
}, },
total total
@ -57,6 +62,17 @@ final class GraphQlClient {
self::checkData($data); self::checkData($data);
$events = $data['data']['events']['elements']; $events = $data['data']['events']['elements'];
foreach ($events as &$event) {
if ($event['picture']) {
$picture_response = self::download_image($event['picture']['url']);
if ($picture_response !== false) {
$picture_encoded = 'data:' . $event['picture']['contentType'] . ';base64,' . base64_encode($picture_response);
$event['picture']['base64'] = $picture_encoded;
}
}
unset($event);
}
EventsCache::set(['url' => $url, 'query' => $query, 'limit' => $limit], $events); EventsCache::set(['url' => $url, 'query' => $query, 'limit' => $limit], $events);
return $events; return $events;
} }
@ -75,6 +91,11 @@ final class GraphQlClient {
physicalAddress { physicalAddress {
description, description,
locality locality
},
picture {
alt,
contentType,
url
} }
}, },
total total
@ -95,6 +116,18 @@ final class GraphQlClient {
self::checkData($data); self::checkData($data);
$events = $data['data']['group']['organizedEvents']['elements']; $events = $data['data']['group']['organizedEvents']['elements'];
foreach ($events as &$event) {
if ($event['picture']) {
$picture_response = self::download_image($event['picture']['url']);
if ($picture_response !== false) {
$picture_encoded = 'data:' . $event['picture']['contentType'] . ';base64,' . base64_encode($picture_response);
$event['picture']['base64'] = $picture_encoded;
}
}
unset($event);
}
EventsCache::set(['url' => $url, 'query' => $query, 'afterDatetime' => $afterDatetime, 'groupName' => $groupName, 'limit' => $limit], $events); EventsCache::set(['url' => $url, 'query' => $query, 'afterDatetime' => $afterDatetime, 'groupName' => $groupName, 'limit' => $limit], $events);
return $events; return $events;
} }
@ -110,4 +143,28 @@ final class GraphQlClient {
} }
} }
} }
private static function download_image($url) {
// Initialize curl handle
$ch = curl_init($url);
// Set curl options
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 60); // Set timeout to 60 seconds (adjust as needed)
// Execute the request
$image_data = curl_exec($ch);
// Check for errors
if (curl_errno($ch)) {
print_r(curl_error($ch));
throw new \Error('Error: ' . curl_error($ch));
}
// Close curl handle
curl_close($ch);
return $image_data;
}
} }

View File

@ -23,6 +23,8 @@ Features
- Set the URL of the Mobilizon instance in the settings - Set the URL of the Mobilizon instance in the settings
- Toggle adding named offset in brackets after the time in the settings - Toggle adding named offset in brackets after the time in the settings
This plugin requests the events via Mobilizon's GraphQL API.
The source code is available on [Github](https://github.com/dwaxweiler/connector-mobilizon). The source code is available on [Github](https://github.com/dwaxweiler/connector-mobilizon).
## Installation ## Installation
@ -41,6 +43,38 @@ You have to use their username, e.g. `@nosliensvivants`, and append the name of
## Changelog ## Changelog
### [1.4.0]
#### Changed
- Update dependencies
- Confirm compatibility with WordPress 6.8
### [1.3.0]
#### Added
- Comment for translators what placeholder will contain
#### Changed
- Confirm compatibility with WordPress 6.7
- Load block script only in footer to reduce waiting time
- Update dependencies
#### Fixed
- Mark event-related data as non-translatable within plugin
- Add version number to script registration to break browser caching
- Handle location being null
### [1.2.0]
#### Added
- Display event picture if available
#### Changed
- Update dependencies
### [1.1.0]
#### Added
- Add some spacing between event items
#### Changed
- Update dependencies
- Confirm compatibility with WordPress 6.6
#### Fixed
- Fix undefined variable $classNamePrefix for both error views
### [1.0.0] ### [1.0.0]
#### Added #### Added
- Display name of group when it cannot be found - Display name of group when it cannot be found

View File

@ -7,5 +7,8 @@ if (!defined('ABSPATH')) {
} }
?> ?>
<div class="<?php echo esc_attr($classNamePrefix); ?>_events-list"> <div class="<?php echo esc_attr($classNamePrefix); ?>_events-list">
<?php echo esc_html(sprintf(__('The group "%s" could not be found!', 'connector-mobilizon'), $groupName)); ?> <?php
/* translators: %s is replaced with the name of the group. */
echo esc_html(sprintf(__('The group "%s" could not be found!', 'connector-mobilizon'), $groupName));
?>
</div> </div>

View File

@ -8,14 +8,17 @@ if (!defined('ABSPATH')) {
?> ?>
<div class="<?php echo esc_attr($classNamePrefix); ?>_events-list"> <div class="<?php echo esc_attr($classNamePrefix); ?>_events-list">
<ul style="list-style-type: none; padding-left: 0;"> <ul style="list-style-type: none; padding-left: 0;">
<?php foreach($events as $event) { ?> <?php foreach ($events as $event) { ?>
<li> <li style="line-height: 150%; margin-top: 20px;">
<a href="<?php echo esc_attr($event['url']); ?>"><?php echo esc_html_e($event['title']); ?></a> <?php if (isset($event['picture'])) { ?>
<img alt="<?php echo esc_attr($event['picture']['alt']); ?>" src="<?php echo esc_attr($event['picture']['base64']); ?>" style="display: block; max-width: 100%;">
<?php } ?>
<a href="<?php echo esc_attr($event['url']); ?>"><?php echo esc_html($event['title']); ?></a>
<br> <br>
<?php echo esc_html_e(Formatter::format_date($locale, $timeZone, $event['beginsOn'], $event['endsOn'], $isShortOffsetNameShown)); ?> <?php echo esc_html(Formatter::format_date($locale, $timeZone, $event['beginsOn'], $event['endsOn'], $isShortOffsetNameShown)); ?>
<?php if (isset($event['physicalAddress'])) { ?> <?php if (isset($event['physicalAddress'])) { ?>
<br> <br>
<?php echo esc_html_e(Formatter::format_location($event['physicalAddress']['description'], $event['physicalAddress']['locality'])) ?> <?php echo esc_html(Formatter::format_location($event['physicalAddress']['description'], $event['physicalAddress']['locality'])) ?>
<?php } ?> <?php } ?>
</li> </li>
<?php } ?> <?php } ?>

View File

@ -42,6 +42,10 @@ final class FormatterTest extends PHPUnit\Framework\TestCase
$this->assertSame('a', Formatter::format_location('a', '')); $this->assertSame('a', Formatter::format_location('a', ''));
} }
public function testLocationFormatDescriptionOnlyWithNull(): void {
$this->assertSame('a', Formatter::format_location('a', null));
}
public function testLocationFormatDescriptionWithSpaceOnly(): void { public function testLocationFormatDescriptionWithSpaceOnly(): void {
$this->assertSame('', Formatter::format_location(' ', '')); $this->assertSame('', Formatter::format_location(' ', ''));
} }