diff --git a/.github/workflows/phpcs_activity.yml b/.github/workflows/phpcs_activity.yml
new file mode 100644
index 0000000..5e9a7d0
--- /dev/null
+++ b/.github/workflows/phpcs_activity.yml
@@ -0,0 +1,43 @@
+name: PHP_CodeSniffer - civiremote_activity
+
+on:
+ push: ~
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ phpcs:
+ runs-on: ubuntu-latest
+ name: PHP_CodeSniffer
+ defaults:
+ run:
+ working-directory: modules/civiremote_activity
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.2
+ coverage: none
+ tools: cs2pr
+ env:
+ fail-fast: true
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+
+ - name: Cache dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('modules/civiremote_activity/tools/phpcs/composer.json') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer update --no-progress --prefer-dist
+
+ - name: Run PHP_CodeSniffer
+ run: composer phpcs -- -q --report=checkstyle | cs2pr
diff --git a/.github/workflows/phpstan_activity.yml b/.github/workflows/phpstan_activity.yml
new file mode 100644
index 0000000..eb0d116
--- /dev/null
+++ b/.github/workflows/phpstan_activity.yml
@@ -0,0 +1,49 @@
+name: PHPStan - civiremote_activity
+
+on:
+ push: ~
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ phpstan:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php-versions: ['7.4', '8.0', '8.2']
+ prefer: ['prefer-stable', 'prefer-lowest']
+ name: PHPStan with PHP ${{ matrix.php-versions }} ${{ matrix.prefer }}
+ defaults:
+ run:
+ working-directory: modules/civiremote_activity
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+ coverage: none
+ env:
+ fail-fast: true
+
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+
+ - name: Cache dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ matrix.prefer }}-${{ hashFiles('modules/civiremote_activity/**/composer.json') }}
+ restore-keys: ${{ runner.os }}-composer-${{ matrix.prefer }}-
+
+ - name: Install dependencies
+ run: |
+ composer update --no-progress --prefer-dist --${{ matrix.prefer }} --optimize-autoloader &&
+ composer composer-phpstan -- update --no-progress --prefer-dist --optimize-autoloader &&
+ composer --working-dir=ci update --no-progress --prefer-dist --${{ matrix.prefer }} --optimize-autoloader
+
+ - name: Run PHPStan
+ run: composer phpstan -- analyse -c phpstan.ci.neon
diff --git a/modules/civiremote_activity/ci/README.md b/modules/civiremote_activity/ci/README.md
new file mode 100644
index 0000000..0bfc584
--- /dev/null
+++ b/modules/civiremote_activity/ci/README.md
@@ -0,0 +1,2 @@
+The dependencies specified in composer.json of this directory are required to
+run phpstan in CI.
diff --git a/modules/civiremote_activity/ci/composer.json b/modules/civiremote_activity/ci/composer.json
new file mode 100644
index 0000000..2e52df8
--- /dev/null
+++ b/modules/civiremote_activity/ci/composer.json
@@ -0,0 +1,29 @@
+{
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "sort-packages": true
+ },
+ "repositories": [
+ {
+ "type": "vcs",
+ "url": "https://github.com/systopia/drupal-json_forms.git"
+ },
+ {
+ "type": "vcs",
+ "url": "https://github.com/systopia/opis-json-schema-ext.git"
+ },
+ {
+ "type": "vcs",
+ "url": "https://github.com/systopia/expression-language-ext.git"
+ },
+ {
+ "type": "composer",
+ "url": "https://packages.drupal.org/8"
+ }
+ ],
+ "require": {
+ "drupal/core": "^9.5 || ^10",
+ "drupal/json_forms": "~0.1"
+ }
+}
diff --git a/modules/civiremote_activity/civiremote_activity.info.yml b/modules/civiremote_activity/civiremote_activity.info.yml
new file mode 100644
index 0000000..93ae7be
--- /dev/null
+++ b/modules/civiremote_activity/civiremote_activity.info.yml
@@ -0,0 +1,10 @@
+name: CiviRemote Activity
+type: module
+description: 'CiviRemote Activity'
+package: CiviCRM
+core_version_requirement: ^9.5 || ^10
+dependencies:
+ - civiremote_entity
+
+project: civiremote
+version: 0.1-dev
diff --git a/modules/civiremote_activity/civiremote_activity.routing.yml b/modules/civiremote_activity/civiremote_activity.routing.yml
new file mode 100644
index 0000000..f102ef7
--- /dev/null
+++ b/modules/civiremote_activity/civiremote_activity.routing.yml
@@ -0,0 +1,25 @@
+civiremote_activity.create_form:
+ path: '/civiremote/activity/{profile}/add'
+ defaults:
+ _controller: 'Drupal\civiremote_activity\Controller\ActivityCreateController::form'
+ options:
+ no_cache: TRUE
+ parameters:
+ profile:
+ type: string
+ requirements:
+ _user_is_logged_in: 'TRUE'
+
+civiremote_activity.update_form:
+ path: '/civiremote/activity/{profile}/{id}'
+ defaults:
+ _controller: 'Drupal\civiremote_activity\Controller\ActivityUpdateController::form'
+ options:
+ no_cache: TRUE
+ parameters:
+ profile:
+ type: string
+ id:
+ type: int
+ requirements:
+ _user_is_logged_in: 'TRUE'
diff --git a/modules/civiremote_activity/civiremote_activity.services.yml b/modules/civiremote_activity/civiremote_activity.services.yml
new file mode 100644
index 0000000..09c90eb
--- /dev/null
+++ b/modules/civiremote_activity/civiremote_activity.services.yml
@@ -0,0 +1,23 @@
+services:
+ _defaults:
+ autowire: true
+ public: false # Controller classes and services directly fetched from container need to be public
+
+ Drupal\civiremote_activity\Api\ActivityApi:
+ class: Drupal\civiremote_activity\Api\ActivityApi
+
+ Drupal\civiremote_activity\Controller\ActivityCreateController:
+ class: Drupal\civiremote_activity\Controller\ActivityUpdateController
+ public: true
+
+ Drupal\civiremote_activity\Controller\ActivityUpdateController:
+ class: Drupal\civiremote_activity\Controller\ActivityUpdateController
+ public: true
+
+ Drupal\civiremote_activity\Form\RequestHandler\ActivityCreateFormRequestHandler:
+ class: Drupal\civiremote_activity\Form\RequestHandler\ActivityCreateFormRequestHandler
+ public: true
+
+ Drupal\civiremote_activity\Form\RequestHandler\ActivityUpdateFormRequestHandler:
+ class: Drupal\civiremote_activity\Form\RequestHandler\ActivityUpdateFormRequestHandler
+ public: true
diff --git a/modules/civiremote_activity/composer.json b/modules/civiremote_activity/composer.json
new file mode 100644
index 0000000..f701f6b
--- /dev/null
+++ b/modules/civiremote_activity/composer.json
@@ -0,0 +1,68 @@
+{
+ "name": "custom/civiremote_activity",
+ "description": "Drupal frontend to access CiviCRM Remote Activity.",
+ "type": "drupal-custom-module",
+ "license": "AGPL-3.0-only",
+ "authors": [
+ {
+ "name": "SYSTOPIA GmbH",
+ "email": "info@systopia.de"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "Drupal\\civiremote_activity\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Drupal\\Tests\\civiremote_activity\\": "tests/src/"
+ }
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "sort-packages": true,
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true,
+ "phpstan/extension-installer": true
+ }
+ },
+ "repositories": [
+ {
+ "type": "composer",
+ "url": "https://packages.drupal.org/8"
+ }
+ ],
+ "require": {
+ "php": "^7.4 || ^8"
+ },
+ "require-dev": {
+ "drupal/core-dev": "^9.5 || ^10"
+ },
+ "scripts": {
+ "composer-phpstan": [
+ "@composer --working-dir=tools/phpstan"
+ ],
+ "composer-tools": [
+ "@composer-phpstan"
+ ],
+ "phpcs": [
+ "@php vendor/bin/phpcs"
+ ],
+ "phpcbf": [
+ "@php vendor/bin/phpcbf"
+ ],
+ "phpstan": [
+ "@php tools/phpstan/vendor/bin/phpstan"
+ ],
+ "phpunit": [
+ "@php vendor/bin/phpunit --coverage-text"
+ ],
+ "test": [
+ "@phpcs",
+ "@phpstan",
+ "@phpunit"
+ ]
+ }
+}
diff --git a/modules/civiremote_activity/phpcs.xml.dist b/modules/civiremote_activity/phpcs.xml.dist
new file mode 100644
index 0000000..64633e5
--- /dev/null
+++ b/modules/civiremote_activity/phpcs.xml.dist
@@ -0,0 +1,82 @@
+
+
+
+ src
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/civiremote_activity/phpstan.ci.neon b/modules/civiremote_activity/phpstan.ci.neon
new file mode 100644
index 0000000..d051f46
--- /dev/null
+++ b/modules/civiremote_activity/phpstan.ci.neon
@@ -0,0 +1,8 @@
+includes:
+ - phpstan.neon.dist
+
+parameters:
+ bootstrapFiles:
+ - ci/vendor/autoload.php
+ scanFiles:
+ - ci/vendor/drupal/core/tests/Drupal/Tests/UnitTestCase.php
diff --git a/modules/civiremote_activity/phpstan.neon.dist b/modules/civiremote_activity/phpstan.neon.dist
new file mode 100644
index 0000000..4344a63
--- /dev/null
+++ b/modules/civiremote_activity/phpstan.neon.dist
@@ -0,0 +1,28 @@
+parameters:
+ paths:
+ - src
+ #- tests
+ #- civiremote_activity.module
+ bootstrapFiles:
+ - vendor/autoload.php
+ scanDirectories:
+ - ../civiremote_entity/src
+ level: 9
+ checkTooWideReturnTypesInProtectedAndPublicMethods: true
+ checkUninitializedProperties: true
+ checkMissingCallableSignature: true
+ treatPhpDocTypesAsCertain: false
+ exceptions:
+ check:
+ missingCheckedExceptionInThrows: true
+ tooWideThrowType: true
+ checkedExceptionClasses:
+ - \Assert\AssertionFailedException
+ implicitThrows: false
+ ignoreErrors:
+ # Note paths are prefixed with ""*/" to work with inspections in PHPStorm because of:
+ # https://youtrack.jetbrains.com/issue/WI-63891/PHPStan-ignoreErrors-configuration-isnt-working-with-inspections
+ # Happens in classes implementing ContainerInjectionInterface::create()
+ - '/ constructor expects [^\s]+, object(\|null)? given.$/'
+
+ tmpDir: .phpstan
diff --git a/modules/civiremote_activity/phpunit.xml.dist b/modules/civiremote_activity/phpunit.xml.dist
new file mode 100644
index 0000000..6545ac2
--- /dev/null
+++ b/modules/civiremote_activity/phpunit.xml.dist
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tests/src/Unit
+
+
+
+
+
+
+
+
+
+
+
+ src
+
+
+
+
diff --git a/modules/civiremote_activity/src/Api/ActivityApi.php b/modules/civiremote_activity/src/Api/ActivityApi.php
new file mode 100644
index 0000000..d4b663a
--- /dev/null
+++ b/modules/civiremote_activity/src/Api/ActivityApi.php
@@ -0,0 +1,31 @@
+.
+ */
+
+declare(strict_types = 1);
+
+namespace Drupal\civiremote_activity\Api;
+
+use Drupal\civiremote_entity\Api\AbstractEntityApi;
+
+final class ActivityApi extends AbstractEntityApi {
+
+ protected function getRemoteEntityName(): string {
+ return 'RemoteActivity';
+ }
+
+}
diff --git a/modules/civiremote_activity/src/Controller/ActivityCreateController.php b/modules/civiremote_activity/src/Controller/ActivityCreateController.php
new file mode 100644
index 0000000..141cca2
--- /dev/null
+++ b/modules/civiremote_activity/src/Controller/ActivityCreateController.php
@@ -0,0 +1,38 @@
+.
+ */
+
+declare(strict_types = 1);
+
+namespace Drupal\civiremote_activity\Controller;
+
+use Drupal\civiremote_activity\Form\ActivityCreateForm;
+use Drupal\Core\Controller\ControllerBase;
+
+final class ActivityCreateController extends ControllerBase {
+
+ /**
+ * @phpstan-return array JSON serializable.
+ */
+ public function form(string $profile): array {
+ $form = $this->formBuilder()->getForm(ActivityCreateForm::class);
+ $form['#title'] ??= $this->t('CiviRemote Activity Create');
+
+ return $form;
+ }
+
+}
diff --git a/modules/civiremote_activity/src/Controller/ActivityUpdateController.php b/modules/civiremote_activity/src/Controller/ActivityUpdateController.php
new file mode 100644
index 0000000..410274d
--- /dev/null
+++ b/modules/civiremote_activity/src/Controller/ActivityUpdateController.php
@@ -0,0 +1,38 @@
+.
+ */
+
+declare(strict_types = 1);
+
+namespace Drupal\civiremote_activity\Controller;
+
+use Drupal\civiremote_activity\Form\ActivityUpdateForm;
+use Drupal\Core\Controller\ControllerBase;
+
+final class ActivityUpdateController extends ControllerBase {
+
+ /**
+ * @phpstan-return array JSON serializable.
+ */
+ public function form(string $profile, int $id): array {
+ $form = $this->formBuilder()->getForm(ActivityUpdateForm::class);
+ $form['#title'] ??= $this->t('CiviRemote Activity Update');
+
+ return $form;
+ }
+
+}
diff --git a/modules/civiremote_activity/src/Form/ActivityCreateForm.php b/modules/civiremote_activity/src/Form/ActivityCreateForm.php
new file mode 100644
index 0000000..c928060
--- /dev/null
+++ b/modules/civiremote_activity/src/Form/ActivityCreateForm.php
@@ -0,0 +1,52 @@
+.
+ */
+
+declare(strict_types=1);
+
+namespace Drupal\civiremote_activity\Form;
+
+use Drupal\civiremote_activity\Form\RequestHandler\ActivityCreateFormRequestHandler;
+use Drupal\civiremote_entity\Form\AbstractEntityForm;
+use Drupal\civiremote_entity\Form\ResponseHandler\FormResponseHandlerInterface;
+use Drupal\json_forms\Form\FormArrayFactoryInterface;
+use Drupal\json_forms\Form\Validation\FormValidationMapperInterface;
+use Drupal\json_forms\Form\Validation\FormValidatorInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+final class ActivityCreateForm extends AbstractEntityForm {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return static
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get(FormArrayFactoryInterface::class),
+ $container->get(FormValidatorInterface::class),
+ $container->get(FormValidationMapperInterface::class),
+ $container->get(ActivityCreateFormRequestHandler::class),
+ $container->get(FormResponseHandlerInterface::class),
+ );
+ }
+
+ public function getFormId(): string {
+ return 'civiremote_activity_create_form';
+ }
+
+}
diff --git a/modules/civiremote_activity/src/Form/ActivityUpdateForm.php b/modules/civiremote_activity/src/Form/ActivityUpdateForm.php
new file mode 100644
index 0000000..3974ca7
--- /dev/null
+++ b/modules/civiremote_activity/src/Form/ActivityUpdateForm.php
@@ -0,0 +1,52 @@
+.
+ */
+
+declare(strict_types=1);
+
+namespace Drupal\civiremote_activity\Form;
+
+use Drupal\civiremote_activity\Form\RequestHandler\ActivityUpdateFormRequestHandler;
+use Drupal\civiremote_entity\Form\AbstractEntityForm;
+use Drupal\civiremote_entity\Form\ResponseHandler\FormResponseHandlerInterface;
+use Drupal\json_forms\Form\FormArrayFactoryInterface;
+use Drupal\json_forms\Form\Validation\FormValidationMapperInterface;
+use Drupal\json_forms\Form\Validation\FormValidatorInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+final class ActivityUpdateForm extends AbstractEntityForm {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return static
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get(FormArrayFactoryInterface::class),
+ $container->get(FormValidatorInterface::class),
+ $container->get(FormValidationMapperInterface::class),
+ $container->get(ActivityUpdateFormRequestHandler::class),
+ $container->get(FormResponseHandlerInterface::class),
+ );
+ }
+
+ public function getFormId(): string {
+ return 'civiremote_activity_update_form';
+ }
+
+}
diff --git a/modules/civiremote_activity/src/Form/RequestHandler/ActivityCreateFormRequestHandler.php b/modules/civiremote_activity/src/Form/RequestHandler/ActivityCreateFormRequestHandler.php
new file mode 100644
index 0000000..82b2f3b
--- /dev/null
+++ b/modules/civiremote_activity/src/Form/RequestHandler/ActivityCreateFormRequestHandler.php
@@ -0,0 +1,35 @@
+.
+ */
+
+declare(strict_types=1);
+
+namespace Drupal\civiremote_activity\Form\RequestHandler;
+
+use Drupal\civiremote_activity\Api\ActivityApi;
+use Drupal\civiremote_entity\Form\RequestHandler\EntityCreateFormRequestHandler;
+
+final class ActivityCreateFormRequestHandler extends EntityCreateFormRequestHandler {
+
+ // For autowiring:
+ // phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found
+ public function __construct(ActivityApi $activityApi) {
+ // phpcs:enable
+ parent::__construct($activityApi);
+ }
+
+}
diff --git a/modules/civiremote_activity/src/Form/RequestHandler/ActivityUpdateFormRequestHandler.php b/modules/civiremote_activity/src/Form/RequestHandler/ActivityUpdateFormRequestHandler.php
new file mode 100644
index 0000000..25418fb
--- /dev/null
+++ b/modules/civiremote_activity/src/Form/RequestHandler/ActivityUpdateFormRequestHandler.php
@@ -0,0 +1,35 @@
+.
+ */
+
+declare(strict_types=1);
+
+namespace Drupal\civiremote_activity\Form\RequestHandler;
+
+use Drupal\civiremote_activity\Api\ActivityApi;
+use Drupal\civiremote_entity\Form\RequestHandler\EntityUpdateFormRequestHandler;
+
+final class ActivityUpdateFormRequestHandler extends EntityUpdateFormRequestHandler {
+
+ // For autowiring:
+ // phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found
+ public function __construct(ActivityApi $activityApi) {
+ // phpcs:enable
+ parent::__construct($activityApi);
+ }
+
+}
diff --git a/modules/civiremote_activity/tools/phpstan/composer.json b/modules/civiremote_activity/tools/phpstan/composer.json
new file mode 100644
index 0000000..0a77baa
--- /dev/null
+++ b/modules/civiremote_activity/tools/phpstan/composer.json
@@ -0,0 +1,18 @@
+{
+ "require": {
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.7",
+ "phpstan/phpstan-beberlei-assert": "^1.0",
+ "phpstan/phpstan-deprecation-rules": "^1.0",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.2",
+ "thecodingmachine/phpstan-strict-rules": "^1.0",
+ "voku/phpstan-rules": "^3"
+ },
+ "config": {
+ "allow-plugins": {
+ "phpstan/extension-installer": true
+ },
+ "sort-packages": true
+ }
+}