The community loves to promise signatures in pledges. We love signatures. This WordPress plugin helps to push the community to fulfill their promises. It mixes some gamification elements with pressure by control, and it makes achievements visible to the community.
While a first version was developed for the @jungegruene, this is a complete rewrite, that is here to grow.
For folks that just want to use employ the plugin on their site.
- Grab the latest release and install it like any other plugin.
- Head over to
Settings > Collectme
and create a new cause. - Check out the causes settings on the same page.
- Copy the shortcode and paste it into your post or page.
- Visit the post or page and follow the instructions.
- Populate the
wp_collectme_account_tokens
table with the data of the users you want to log in automatically. Thetoken
must contain exactly 64 alpha-numeric characters (e.g. SHA256 hash). - You may then send the users their personal link, which must look like follows:
https://example.com/post-with-collectme-shortcode?action=create&email=<email>&token=<token>
Variant: You may also use the collectme_get_account_token
filter and craft your own token validation function.
See /docs/link-auth-with-mailchimp.md for an example.
The action collectme_after_user_setup
is built for this purpose. It fires every time a user is added to a cause.
Example:
add_action( 'collectme_after_user_setup', function(
\Collectme\Model\Entities\User $user,
string $groupUuid,
string $causeUuid
) {
// add your logic to grab the objective data from an external source
// assign the goal to $count, a string identifying the source to $source
$count = 123;
$source = 'Form XY';
$objective = new \Collectme\Model\Entities\Objective(
null,
$count,
$groupUuid,
$source
);
try {
$objective->save();
} catch (\Collectme\Exceptions\CollectmeDBException $e) {
// it's possibly ok to just ignore the error as the
// user will be prompted by the application to set a
// goal if he hasn't got one yet.
}
}, 10, 3 );
The plugin can send the following email notifications:
Collection reminders:
- Start collecting reminder to users that have created an account but haven't entered any signatures yet.
- Continue collecting reminder to users that haven't entered any signatures for some time (see the Timing section below) and haven't reached their goal. If no goal was set, no reminder is sent.
Goal related:
- Thank you for adding a personal goal to users that have added their first goal.
- Thank you for upgrading your personal goal for users that raised their goal.
- Thank you for achieving your personal goal for users that entered at least as many signatures as their personal goal.
- Thank you for achieving the final goal for users that reached the highest goal.
- Thank you for achieving your personal goal and upgrading it afterwards to users that reached their goal and have set a higher one after.
There is a section Scheduled E-Mails on the settings page of the plugin that controls the timing of the emails. A good starting point is 21 days for the reminder emails and 24 hours for the goal related emails. If the fields are left blank, no emails will be sent.
The mail scheduler also respects the collection start date and the collection end date. No emails are sent before the start and after the end date. If the dates are left blank, the mailer won't stop sending emails.
You can customize the email subject and body using the string override feature on the plugin's settings page.
For the cool kids that want to contribute 😎
The plugin is primarily built as single page application (SPA) using Vue.js and the WordPress REST API.
The REST API is specified in OpenAPI 3.0 format (the specification: docs/api/rest-api.yaml). It is JSON:API Spec compliant.
The settings page and the SPAs home are classic HTML pages and not Vue components. The SPAs home also handles part of the login process (see docs/uml/login-flow.puml).
We use the standard installation of Vue3 with Typescript, Vue Router 4, Pinia for state management and Vite for the build setup.
The sources live in /app and the build files are output to /dist.
To get seamless integration of the Vuejs dev environment with WordPress, we had to get messy with the asset loading. It works as follows:
Unless you set define( 'SCRIPT_DEBUG', true );
in wp-config.php
the static built assets from /dist are
served, as in production.
Run docker-compose run node yarn build
to rebuild the /dist files.
To enable dev mode, add define( 'SCRIPT_DEBUG', true );
to your wp-config.php
. In this setup this is already the
case, via the WORDPRESS_CONFIG_EXTRA
environment variable in docker-compose.yml.
In dev mode, the vue scripts are the loaded directly from the vite dev server, which runs under
localhost:3000 (cf. docker-compose.yml). If you need to change the
hostname or port, use the NODEJS_DEV_SERVER_BASE_URL
environment variable in
the docker-compose.yml.
See AssetLoader::getScriptUrls() for further details. Yes, it is hacky :)
Paths:
- General components live in /app/src/components/base/
- Specific components /app/src/components/specific/{routeName}/
- Components with assets or specific child components are to be packed into a subdirectory with the name of the
component (in
PascalCase
)
Component Naming:
PascalCase
file names- General components names are prefixed with
Base
- Components naming rules:
- At least two words
The
prefix for components, that should only ever have a single active instance (e.g.TheBaseOverlay
)- Tightly coupled or specific child components include the parent component name as prefix.
- Start with the highest level words and put more specific ones after (e.g.
SearchButtonClear.vue
)
HTML Class Naming:
- Wrapping container:
collectme-{component-name}
e.g.collectme-the-base-overlay
- Inner elements:
collectme-{component-name}__{specific-tag}
e.g.collectme-the-base-overlay__title
CSS:
- Don't use the styles
scoped
attribute (so themes can overwrite the plugins styles)
Is set up (vitest and cypress) but not used.
All JS building in done with Yarn in the node container.
# install dependencies
docker-compose run node yarn install
# build for production
docker-compose run node yarn run build
See app/package.json for details.
To get good IDE integration and nice tooling with Composer Autoloading, the PHP parts of the plugin use PSR-4 and PSR-12 instead of the WordPress Coding Standards.
For the sake of testability, we use dependency injection with PHP-DI. Testing is done with PHPUnit.
The plugin need at least PHP 8.1.
# install test environement
docker-compose run wordpress bash -c \
'cd wp-content/plugins/collectme \
&& TMPDIR=$(pwd)/tmp bin/install-wp-tests.sh collectme_test root "" mysql latest'
# run tests
docker-compose run wordpress bash -c \
'cd wp-content/plugins/collectme \
&& php vendor/bin/phpunit'
Set PHP_IDE_CONFIG='serverName=Docker'
in your IDEs run configuration if you want to run tests directly in your IDE.
serverName=Docker
corresponds to the server name configured in your IDE (Settings > PHP > Servers > Name
for
PHPStorm).
Please note also, that the WP_TESTS_DIR="$(pwd)/tmp/wordpress-tests-lib"
environment variable is set in
/phpunit.xml.dist. It must point to the same directory as TMPDIR
, you've used for installing the
tests.
The WordPress container also contains the WP-CLI. Currently it is only used
to extract the plugin translation template (.pot
file) and to compile the .mo
files:
# extract translations
docker-compose run wordpress bash -c 'XDEBUG_MODE=off php -d memory_limit=1G $(which wp) --allow-root i18n make-pot wp-content/plugins/collectme/ wp-content/plugins/collectme/languages/collectme.pot --slug=collectme --domain=collectme --exclude=tmp,vendor --skip-js --skip-block-json --skip-theme-json && chown 1000:1000 wp-content/plugins/collectme/languages/collectme.pot'
# generate mo files
docker-compose run wordpress bash -c 'XDEBUG_MODE=off $(which wp) --allow-root i18n make-mo wp-content/plugins/collectme/languages'
Dependecies are managed with Composer. To install the dependencies, run:
docker-compose run wordpress composer --working-dir=wp-content/plugins/collectme install
Mails sent by the Plugin are caught by Mailhog and accessible under localhost:8020.
In browser editor and preview for the openapi definition of our rest-api.
docker-compose run swagger-editor
Visit localhost:8030
Helpful resources:
- OpenApi Guide
- OpenApi Spec (we use version 3.0.0)
- JSON:API Spec
-
Generate type definitions in /app/src/models/generated
docker-compose run swagger-codegen generate -i /tmp/swagger/input/rest-api.yaml -o /tmp/swagger/output -l typescript-axios sudo chown -R $(id -u):$(id -g) gen sed -i '/import type/! s/import /import type /g' gen/models/*.ts sed -i -r 's/(\s)Date(\s)/\1string\2/g' gen/models/*.ts rsync -av --delete gen/models/ app/src/models/generated
-
Generate API docs in /docs/api/index.html
docker-compose run swagger-codegen generate -i /tmp/swagger/input/rest-api.yaml -o /tmp/swagger/output -l html2 sudo chown -R $(id -u):$(id -g) gen cp gen/index.html docs/api/index.html
Helpful resources:
docker-compose run swagger-codegen langs
docker-compose run swagger-codegen generate
- Swagger-Codegen on GitHub
Localization is done with gettext and Crowdin. The workflow:
- Use translation functions as usual ( see How to Internationalize Your Plugin)
- Create a pull request
- The translation template (.pot file) is generated and uploaded to Crowdin by the GitHub action l10n.yml.
- Translate in Crowdin
- Merge pull request into main
- The translations are downloaded from Crowdin, mo-files are compiled and committed to by the GitHub action l10n.yml.
See also crowdin.yml.
- use app/src/utility/i18n.ts as translation function
- provide the strings in languages/app-strings.php