Compare commits
121 Commits
89de042ef4
...
e2e1c18904
Author | SHA1 | Date |
---|---|---|
Aldo | e2e1c18904 | |
Michael Stenta | fb6fd0d0bc | |
Michael Stenta | 6abe3e73b0 | |
Michael Stenta | 92c558dde3 | |
Michael Stenta | cec7ed04d0 | |
Paul Weidner | a9a6d8008f | |
Paul Weidner | 38271bf84f | |
Michael Stenta | dadf79c8f0 | |
Paul Weidner | 462c82d6ca | |
Michael Stenta | 96bf17bc69 | |
Michael Stenta | 1e2c7c2e47 | |
Michael Stenta | ee71b28ae6 | |
Michael Stenta | 98130dec1c | |
Paul Weidner | 06b930886a | |
Michael Stenta | bd091b7d39 | |
Michael Stenta | a79a2989a8 | |
Michael Stenta | 8bf87f00fe | |
Michael Stenta | 36fc154c47 | |
Michael Stenta | 8d25b32b01 | |
Michael Stenta | 4f4f666c28 | |
Michael Stenta | 79704c3c7c | |
Michael Stenta | 9d31aa5ce7 | |
Michael Stenta | 9c6d597404 | |
Michael Stenta | 9058e39f7c | |
Michael Stenta | 70eb37cbf9 | |
Michael Stenta | 471e2cda6a | |
Michael Stenta | dc00956117 | |
Michael Stenta | 1b2ce0b075 | |
Michael Stenta | 433747d0ff | |
Michael Stenta | f7e6318026 | |
Michael Stenta | cce51328c0 | |
Michael Stenta | efc6797899 | |
Michael Stenta | 3e891a880d | |
Michael Stenta | c0cc0a92b5 | |
Paul Weidner | 65c0159e79 | |
Paul Weidner | 28b2d3b83d | |
Paul Weidner | 604ce5f88e | |
Paul Weidner | 0bb5facc66 | |
Michael Stenta | 4d05465f7c | |
Michael Stenta | 8b8ac5bb20 | |
Michael Stenta | 22024a8f86 | |
Michael Stenta | ee34ca660a | |
Michael Stenta | 0a3a5084c1 | |
Michael Stenta | 6ccf2cda0d | |
Michael Stenta | 9995efd994 | |
Michael Stenta | 59802748e8 | |
Michael Stenta | 410da5d812 | |
Michael Stenta | 5fcb89a16f | |
Michael Stenta | 21d022bfad | |
Michael Stenta | 3cc0c515ef | |
Michael Stenta | 4e22c5fd83 | |
Michael Stenta | 60d2357fbc | |
Michael Stenta | 07882e177a | |
Michael Stenta | 146719f363 | |
Michael Stenta | e869407d45 | |
Michael Stenta | 73c9eef921 | |
Michael Stenta | 09677800a2 | |
Michael Stenta | c8b9fad4b6 | |
Michael Stenta | 067ea5fa05 | |
Paul Weidner | bacf11f295 | |
Paul Weidner | fa5724e035 | |
Paul Weidner | 0e0258b66c | |
Paul Weidner | 0525fd5a49 | |
Paul Weidner | 2f8b3e05e8 | |
Paul Weidner | 268412bd23 | |
Paul Weidner | c4d3f174d5 | |
Paul Weidner | 55e999af27 | |
Paul Weidner | 6dc23e7054 | |
Paul Weidner | abd6ee9646 | |
Paul Weidner | 69d9ef713b | |
Paul Weidner | 71206d03d2 | |
Michael Stenta | 1410fa5ea7 | |
Michael Stenta | 359ee1e79c | |
Paul Weidner | 52f968ef6c | |
Paul Weidner | 1cee2cb2e1 | |
Michael Stenta | d67e4492d1 | |
Paul Weidner | 4d9bd96234 | |
Paul Weidner | 3972ea268f | |
Paul Weidner | e23e5923dc | |
Paul Weidner | d8943488ab | |
Paul Weidner | 681de9f9ea | |
Paul Weidner | 2c4953feff | |
Paul Weidner | 78a78b3aaa | |
Paul Weidner | ad3965208f | |
Paul Weidner | 860790f9d5 | |
Paul Weidner | e5894bdce1 | |
Paul Weidner | 7033224a0d | |
Paul Weidner | a53f2b76e2 | |
Michael Stenta | 104885274c | |
Michael Stenta | a09ef42add | |
Michael Stenta | 6879ff591f | |
Michael Stenta | ce8173f152 | |
Michael Stenta | f5c7139a63 | |
Michael Stenta | a4544a7ecb | |
Michael Stenta | 1cb57c2178 | |
Michael Stenta | 0467595a0a | |
Michael Stenta | ca5298245a | |
Michael Stenta | b074a635c9 | |
Michael Stenta | 5cbd753905 | |
Michael Stenta | 74f3ee4b5d | |
Michael Stenta | a37820e514 | |
Michael Stenta | 31cd1aa177 | |
Michael Stenta | 220c99c510 | |
Paul Weidner | 5a49dce4f5 | |
Michael Stenta | 84421be197 | |
Michael Stenta | 74ffac59ec | |
Michael Stenta | 1c404b5152 | |
Michael Stenta | 755e07bfdf | |
Michael Stenta | 6c03557b3c | |
Michael Stenta | 7329866866 | |
Michael Stenta | 11b256b33a | |
Michael Stenta | 004ecd8e9e | |
Michael Stenta | 2a992f5a7c | |
Michael Stenta | f1c6a47846 | |
Michael Stenta | 31c5280662 | |
Michael Stenta | 8091b51561 | |
Michael Stenta | ca3bc10c7d | |
Michael Stenta | 83b15287ba | |
Michael Stenta | 8f73c593a1 | |
Paul Weidner | 17b792aca7 | |
Michael Stenta | 68ae993c41 |
|
@ -222,8 +222,8 @@ jobs:
|
|||
fi
|
||||
outputs:
|
||||
announce: ${{ env.ANNOUNCE_RELEASE }}
|
||||
announce:
|
||||
name: Announce new release
|
||||
announce-microblog:
|
||||
name: Announce new release on farmOS-microblog
|
||||
if: needs.publish.outputs.announce
|
||||
needs:
|
||||
- build
|
||||
|
@ -234,3 +234,25 @@ jobs:
|
|||
message: '#farmOS ${{ needs.build.outputs.farmos_version }} has been released! https://github.com/farmOS/farmOS/releases/${{ needs.build.outputs.farmos_version }}'
|
||||
secrets:
|
||||
MICROBLOG_DEPLOY_KEY: ${{ secrets.MICROBLOG_DEPLOY_KEY }}
|
||||
announce-discourse:
|
||||
name: Announce new release on farmOS.discourse.group
|
||||
if: needs.publish.outputs.announce
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
- release
|
||||
- publish
|
||||
steps:
|
||||
- name: Discourse API request
|
||||
env:
|
||||
DISCOURSE_API_KEY: ${{ secrets.DISCOURSE_API_KEY }}
|
||||
run: |
|
||||
curl --fail-with-body -X POST "https://farmos.discourse.group/posts/" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Api-Key: ${DISCOURSE_API_KEY}" \
|
||||
-H "Api-Username: mstenta" \
|
||||
-d '{
|
||||
"title": "farmOS ${{ needs.build.outputs.farmos_version }} has been released",
|
||||
"raw": "farmOS [${{ needs.build.outputs.farmos_version }}](https://github.com/farmOS/farmOS/releases/${{ needs.build.outputs.farmos_version }}) has been released.\n\nFor the full release notes, see [CHANGELOG.md](https://github.com/farmOS/farmOS/blob/${{ needs.build.outputs.farmos_version }}/CHANGELOG.md).",
|
||||
"category": 7
|
||||
}'
|
||||
|
|
50
CHANGELOG.md
50
CHANGELOG.md
|
@ -7,6 +7,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [3.1.1] 2024-02-07
|
||||
|
||||
### Fixed
|
||||
|
||||
- [Grant config permissions create quick_form permission #791](https://github.com/farmOS/farmOS/pull/791)
|
||||
- Patch Gin to fix [Issue #3419904: Help text is cutoff at xsmall breakpoint](https://www.drupal.org/node/3419904)
|
||||
|
||||
### Security
|
||||
|
||||
- Update Migrate Tools to 6.0.3 for [SA-CONTRIB-2024-008](https://www.drupal.org/sa-contrib-2024-008)
|
||||
|
||||
## [3.1.0] 2024-02-02
|
||||
|
||||
### Added
|
||||
|
||||
- [Announce new releases on farmOS.discourse.group #780](https://github.com/farmOS/farmOS/pull/780)
|
||||
- [Add a Product asset type and Product type taxonomy #787](https://github.com/farmOS/farmOS/pull/787)
|
||||
- [Inventory quick form #766](https://github.com/farmOS/farmOS/pull/766)
|
||||
- [Add UI for creating instances of quick forms #785](https://github.com/farmOS/farmOS/pull/785)
|
||||
- [Show map on /locations #779](https://github.com/farmOS/farmOS/pull/779)
|
||||
- [Provide a plan_record entity type for plan record relationships with metadata #781](https://github.com/farmOS/farmOS/pull/781)
|
||||
|
||||
### Changed
|
||||
|
||||
- [Recommend running composer update twice #653](https://github.com/farmOS/farmOS/pull/786)
|
||||
- [Edit form UI improvements #770](https://github.com/farmOS/farmOS/pull/770)
|
||||
- [Improve asset and log CSV exports #783](https://github.com/farmOS/farmOS/pull/783)
|
||||
- [Remove "All" from "Items per page" options in Views #776](https://github.com/farmOS/farmOS/pull/776)
|
||||
|
||||
## [3.0.1] 2024-01-18
|
||||
|
||||
### Added
|
||||
|
||||
- [Add min/max options to integer fields in farm_field.factory #768](https://github.com/farmOS/farmOS/pull/768)
|
||||
|
||||
### Changed
|
||||
|
||||
- Allow users with asset view access to see /asset/%id/locations.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [Patch drupal/core to fix Issue #3414883: datetime_timestamp widget does not use default field value #771](https://github.com/farmOS/farmOS/pull/771)
|
||||
- [Fix duplicated revision tab on entities #773](https://github.com/farmOS/farmOS/pull/773)
|
||||
- Improve access checking on location hierarchy forms.
|
||||
|
||||
## [3.0.0] 2024-01-05
|
||||
|
||||
This is the first "stable" release of farmOS v3. See the release notes for
|
||||
|
@ -662,7 +707,10 @@ moving forward.
|
|||
Drupal 7, which required a complete refactor of the codebase. By comparison,
|
||||
updating from Drupal 9 to 10 will simply involve updating deprecated code.
|
||||
|
||||
[Unreleased]: https://github.com/farmOS/farmOS/compare/3.0.0...HEAD
|
||||
[Unreleased]: https://github.com/farmOS/farmOS/compare/3.1.1...HEAD
|
||||
[3.1.0]: https://github.com/farmOS/farmOS/releases/tag/3.1.1
|
||||
[3.1.0]: https://github.com/farmOS/farmOS/releases/tag/3.1.0
|
||||
[3.0.1]: https://github.com/farmOS/farmOS/releases/tag/3.0.1
|
||||
[3.0.0]: https://github.com/farmOS/farmOS/releases/tag/3.0.0
|
||||
[3.0.0-beta3]: https://github.com/farmOS/farmOS/releases/tag/3.0.0-beta3
|
||||
[3.0.0-beta2]: https://github.com/farmOS/farmOS/releases/tag/3.0.0-beta2
|
||||
|
|
|
@ -18,20 +18,20 @@
|
|||
"require": {
|
||||
"cweagans/composer-patches": "^1.6",
|
||||
"drupal/admin_toolbar": "^3.3",
|
||||
"drupal/core": "10.2.1",
|
||||
"drupal/core": "10.2.3",
|
||||
"drupal/config_update": "^2.0@alpha",
|
||||
"drupal/consumers": "^1.15",
|
||||
"drupal/csv_serialization": "^4.0",
|
||||
"drupal/date_popup": "^1.3",
|
||||
"drupal/entity": "1.4",
|
||||
"drupal/entity_browser": "^2.10",
|
||||
"drupal/entity_reference_integrity": "^1.1",
|
||||
"drupal/entity_reference_revisions": "1.10",
|
||||
"drupal/entity_reference_integrity": "1.2",
|
||||
"drupal/entity_reference_revisions": "1.11",
|
||||
"drupal/entity_reference_validators": "^1.0@beta",
|
||||
"drupal/exif_orientation": "^1.2",
|
||||
"drupal/fraction": "^2.3.1",
|
||||
"drupal/geofield": "^1.40",
|
||||
"drupal/gin": "^3.0@rc",
|
||||
"drupal/gin": "3.0-rc9",
|
||||
"drupal/inline_entity_form": "^1.0@RC",
|
||||
"drupal/inspire_tree": "^1.0",
|
||||
"drupal/jsonapi_extras": "^3.22",
|
||||
|
@ -40,7 +40,7 @@
|
|||
"drupal/migrate_plus": "^6.0",
|
||||
"drupal/migrate_source_csv": "^3.5",
|
||||
"drupal/migrate_source_ui": "^1.0",
|
||||
"drupal/migrate_tools": "^6.0.2",
|
||||
"drupal/migrate_tools": "^6.0.3",
|
||||
"drupal/role_delegation": "^1.2",
|
||||
"drupal/simple_oauth": "6.0.0-beta5",
|
||||
"drupal/simple_oauth_password_grant": "^1.0@RC",
|
||||
|
@ -61,14 +61,22 @@
|
|||
"Issue #2429699: Add Views EntityReference filter to be available for all entity reference fields.": "https://www.drupal.org/files/issues/2021-12-02/2429699-453-9.3.x.patch",
|
||||
"Issue #2339235: Remove taxonomy hard dependency on node module": "https://www.drupal.org/files/issues/2024-01-03/2339235-10.2.x-82.patch",
|
||||
"Issue #1874838: Allow exposed blocks to use 'Link display' settings": "https://www.drupal.org/files/issues/2021-11-11/1874838-26.patch",
|
||||
"Issue #2909128: Autocomplete not working on Chrome Android": "https://www.drupal.org/files/issues/2023-07-11/2909128-92.patch"
|
||||
"Issue #2909128: Autocomplete not working on Chrome Android": "https://www.drupal.org/files/issues/2023-07-11/2909128-92.patch",
|
||||
"Issue #3414883: Datetime_timestamp widget does not use default field value": "https://www.drupal.org/files/issues/2024-01-15/3414883-11.patch"
|
||||
},
|
||||
"drupal/entity": {
|
||||
"Issue #3206703: Provide reverse relationships for bundle plugin entity_reference fields.": "https://www.drupal.org/files/issues/2022-05-11/3206703-10.patch"
|
||||
},
|
||||
"drupal/entity_reference_integrity": {
|
||||
"Issue #3418000: Delete action only overridden on first entity type": "https://www.drupal.org/files/issues/2024-01-30/3418000-3.patch"
|
||||
},
|
||||
"drupal/entity_reference_revisions": {
|
||||
"Issue #3267304: Infer target_revision_id to be Latest Revision when Only a target_id is Provided": "https://www.drupal.org/files/issues/2022-05-13/3267304-9.patch"
|
||||
},
|
||||
"drupal/gin": {
|
||||
"Issue #3342164: Remove implicit dependency on node module for gin content form.": "https://www.drupal.org/files/issues/2024-01-15/3342164-6.patch",
|
||||
"Issue #3419904: Help text is cutoff at xsmall breakpoint.": "https://www.drupal.org/files/issues/2024-02-07/3419904-4.patch"
|
||||
},
|
||||
"drupal/jsonapi_schema": {
|
||||
"Issue #3256795: Float fields have a null schema": "https://www.drupal.org/files/issues/2022-01-03/3256795-4.patch",
|
||||
"Issue #3246251: Change format utc-millisec to date-time": "https://www.drupal.org/files/issues/2021-10-27/3246251-2.patch",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"require": {
|
||||
"cweagans/composer-patches": "^1.7",
|
||||
"drupal/core-composer-scaffold": "10.2.1"
|
||||
"drupal/core-composer-scaffold": "10.2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"behat/mink": "^1.10",
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
Follow these instructions to set up a local farmOS development environment.
|
||||
|
||||
The only requirements are [Docker](https://www.docker.com) and
|
||||
[Docker Compose](https://docs.docker.com/compose).
|
||||
The only requirement is [Docker](https://www.docker.com).
|
||||
|
||||
## 1. Set up Docker containers
|
||||
|
||||
|
|
|
@ -142,6 +142,8 @@ Both methods expect an array of field definition options. These include:
|
|||
- `integer` - Integer number. Additional options:
|
||||
- `size` (optional) - The integer database column size (`tiny`,
|
||||
`small`, `medium`, `normal`, or `big`). Defaults to `normal`.
|
||||
- `min` (optional) - The minimum value.
|
||||
- `max` (optional) - The maximum value.
|
||||
- `list_string` - Select list with allowed values. Additional options:
|
||||
- `allowed_values` - An associative array of allowed values.
|
||||
- `allowed_values_function` - The name of a function that returns an
|
||||
|
|
|
@ -2,10 +2,24 @@
|
|||
|
||||
## CSV
|
||||
|
||||
All [Asset](/guide/assets), [Log](/guide/logs), and
|
||||
[Quantity](/guide/quantities) lists in farmOS include an "Export CSV" link at
|
||||
the bottom that will generate and download a CSV file. Any sorts or filters
|
||||
that are applied to the list will be represented in the CSV output.
|
||||
All [Asset](/guide/assets) and [Log](/guide/logs) lists in farmOS provide an
|
||||
"Export CSV" action that will generate a CSV of selected records. These include
|
||||
most of the record's information, including columns that are not visible in the
|
||||
list pages themselves.
|
||||
|
||||
[Quantity](/guide/quantities) lists provide an "Export CSV" link at the bottom of the page
|
||||
that serve a similar purpose. These exports include all of the columns that are
|
||||
visible on the Quantity list page, including information about the Quantity
|
||||
itself, as well as some information about the Log records that the Quantity
|
||||
is attached to.
|
||||
|
||||
Any sorts or filters that are applied to the list will be represented in the
|
||||
CSV output.
|
||||
|
||||
**Warning: CSV exports do not include all data.**
|
||||
|
||||
The [farmOS API](/development/api) is the best way to get access to all raw data
|
||||
in a farmOS instance.
|
||||
|
||||
## KML
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ Roles can be "managed" or "unmanaged." The permissions of managed roles are
|
|||
controlled by modules and cannot be modified through the UI. Unmanaged roles
|
||||
can be added/edited through the UI.
|
||||
|
||||
Three managed roles are provided with farmOS:
|
||||
Three default managed roles are provided with farmOS:
|
||||
|
||||
- **Manager** - Has access to everything in farmOS. They can create, edit, and
|
||||
delete records, and they can change configuration settings.
|
||||
|
@ -22,6 +22,12 @@ Three managed roles are provided with farmOS:
|
|||
|
||||
These roles can be disabled by uninstalling the "Default Roles" module.
|
||||
|
||||
The "farmOS Account Admin Role" module provides another optional managed role
|
||||
called **Account Admin**, which has permission to add/edit/remove other users.
|
||||
This is useful in situations where an instance administrator wants to give
|
||||
someone the ability to set up other accounts, without giving them full admin
|
||||
access.
|
||||
|
||||
Permissions for managed roles cannot be modified through the UI. This is not
|
||||
generally an issue since the provided roles have been carefully tailored to
|
||||
work for most applications. In some cases, you may want to further customize
|
||||
|
|
|
@ -171,6 +171,11 @@ farmOS instance via the web UI or by running `drush en mymodule`.
|
|||
Composer provides a simple way to update project dependencies:
|
||||
|
||||
composer update --no-dev
|
||||
composer update --no-dev
|
||||
|
||||
**Note: It is necessary to run this command twice to ensure all dependencies
|
||||
are properly updated.** We have an issue open to figure out a better solution:
|
||||
[Composer merge plugin dependencies are not correctly updated #653](https://github.com/farmOS/farmOS/issues/653).
|
||||
|
||||
This will check for newer versions of all your project's dependencies (based
|
||||
on the version constraints in your `composer.json` file), install them, and
|
||||
|
|
|
@ -66,16 +66,14 @@ These resources may be helpful:
|
|||
Nginx reverse proxy with self-signed certificates for local farmOS
|
||||
development with HTTPS.
|
||||
|
||||
### API Keys
|
||||
### Satellite map layers
|
||||
|
||||
Optional modules are available for adding satellite imagery layers to maps (eg:
|
||||
Mapbox, Google Maps, etc). However, because these layers are hosted by
|
||||
third-party providers, API keys are required to use them. Instructions for
|
||||
obtaining API keys are available via the links below. API keys can be entered
|
||||
into farmOS by going to Settings > Map.
|
||||
|
||||
- [Mapbox](https://docs.mapbox.com/help/how-mapbox-works/access-tokens)
|
||||
- [Google Maps](https://developers.google.com/maps/documentation/javascript/get-api-key)
|
||||
farmOS includes an optional [Mapbox](https://www.mapbox.com) module that can be
|
||||
enabled to add satellite imagery layers to the map. A Mapbox API key is
|
||||
required. For more information, see Mapbox's official documentation:
|
||||
[Access tokens](https://docs.mapbox.com/help/how-mapbox-works/access-tokens).
|
||||
Enable the Mapbox module at Setup > Modules, and then add the API key at
|
||||
Setup > Settings > Map > Mapbox.
|
||||
|
||||
## farmOS Codebase
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ included with farmOS define the following Asset types:
|
|||
- Sensor
|
||||
- Water
|
||||
- Material
|
||||
- Product
|
||||
- Group*
|
||||
|
||||
*Group Assets are unique in that they can "contain" other Assets as "group
|
||||
|
@ -272,6 +273,12 @@ Plant Assets have the following additional relationships:
|
|||
- Plant type (references a Term in the "Plant type" vocabulary)
|
||||
- Season (references a Term in the "Season" vocabulary)
|
||||
|
||||
#### Product Assets
|
||||
|
||||
Product Assets have the following additional relationships:
|
||||
|
||||
- Product type (references a Term in the "Product type" vocabulary)
|
||||
|
||||
#### Sensor Assets
|
||||
|
||||
Sensor Assets have an additional "Data streams" relationship, which is used to
|
||||
|
|
|
@ -20,6 +20,7 @@ is enabled. The modules included with farmOS define the following vocabularies:
|
|||
- Log category
|
||||
- Material type
|
||||
- Plant type
|
||||
- Product type
|
||||
- Season
|
||||
- Unit
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ function farm_modules() {
|
|||
'farm_inventory' => t('Inventory management'),
|
||||
'farm_material' => t('Material assets'),
|
||||
'farm_seed' => t('Seed assets'),
|
||||
'farm_product' => t('Product assets'),
|
||||
'farm_sensor' => t('Sensor assets'),
|
||||
'farm_compost' => t('Compost assets'),
|
||||
'farm_group' => t('Group assets'),
|
||||
|
@ -55,9 +56,11 @@ function farm_modules() {
|
|||
'farm_lab_test' => t('Lab test logs'),
|
||||
'farm_birth' => t('Birth logs'),
|
||||
'farm_medical' => t('Medical logs'),
|
||||
'farm_export_csv' => t('CSV exporter'),
|
||||
'farm_import_csv' => t('CSV importer'),
|
||||
'farm_kml' => t('KML export features'),
|
||||
'farm_export_kml' => t('KML exporter'),
|
||||
'farm_import_kml' => t('KML asset importer'),
|
||||
'farm_map_mapbox' => t('Mapbox map layers: Satellite, Outdoors'),
|
||||
'farm_api_default_consumer' => t('Default API Consumer'),
|
||||
'farm_fieldkit' => t('Field Kit integration'),
|
||||
'farm_l10n' => t('Translation/localization features'),
|
||||
|
|
|
@ -32,3 +32,15 @@ function farm_equipment_farm_entity_bundle_field_info(EntityTypeInterface $entit
|
|||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_farm_ui_theme_field_group_items().
|
||||
*/
|
||||
function farm_equipment_farm_ui_theme_field_group_items(string $entity_type, string $bundle) {
|
||||
if ($entity_type == 'log') {
|
||||
return [
|
||||
'equipment' => 'asset',
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -6,13 +6,10 @@ dependencies:
|
|||
- image.style.thumbnail
|
||||
module:
|
||||
- asset
|
||||
- csv_serialization
|
||||
- farm_group
|
||||
- farm_location
|
||||
- image
|
||||
- options
|
||||
- rest
|
||||
- serialization
|
||||
- state_machine
|
||||
- user
|
||||
id: farm_group_members
|
||||
|
@ -621,7 +618,7 @@ display:
|
|||
items_per_page: true
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '25, 50, 100, 250, 500'
|
||||
items_per_page_options_all: true
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
|
@ -1052,17 +1049,6 @@ display:
|
|||
plugin_id: result
|
||||
empty: false
|
||||
content: 'Displaying @start - @end of @total'
|
||||
display_link:
|
||||
id: display_link
|
||||
table: views
|
||||
field: display_link
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
plugin_id: display_link
|
||||
label: 'Export CSV'
|
||||
empty: false
|
||||
display_id: csv
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
|
@ -1073,61 +1059,6 @@ display:
|
|||
- url.query_args
|
||||
- user.permissions
|
||||
tags: { }
|
||||
csv:
|
||||
id: csv
|
||||
display_title: 'CSV export (rest)'
|
||||
display_plugin: rest_export
|
||||
position: 2
|
||||
display_options:
|
||||
pager:
|
||||
type: none
|
||||
options:
|
||||
offset: 0
|
||||
style:
|
||||
type: serializer
|
||||
options:
|
||||
uses_fields: false
|
||||
formats:
|
||||
csv: csv
|
||||
row:
|
||||
type: data_field
|
||||
options:
|
||||
field_options:
|
||||
asset_bulk_form:
|
||||
alias: ''
|
||||
raw_output: false
|
||||
image_target_id:
|
||||
alias: ''
|
||||
raw_output: false
|
||||
id:
|
||||
alias: ''
|
||||
raw_output: false
|
||||
name:
|
||||
alias: ''
|
||||
raw_output: false
|
||||
type:
|
||||
alias: ''
|
||||
raw_output: false
|
||||
flag_value:
|
||||
alias: ''
|
||||
raw_output: false
|
||||
status:
|
||||
alias: ''
|
||||
raw_output: false
|
||||
display_description: ''
|
||||
display_extenders: { }
|
||||
path: assets.csv
|
||||
auth:
|
||||
- cookie
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- request_format
|
||||
- url
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page:
|
||||
id: page
|
||||
display_title: 'Group members (page)'
|
||||
|
|
|
@ -71,3 +71,34 @@ function farm_group_farm_ui_theme_region_items(string $entity_type) {
|
|||
}
|
||||
return $region_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_farm_ui_theme_field_groups().
|
||||
*/
|
||||
function farm_group_farm_ui_theme_field_groups(string $entity_type, string $bundle) {
|
||||
|
||||
// Add a field group for group membership fields on logs.
|
||||
if ($entity_type == 'log') {
|
||||
return [
|
||||
'group' => [
|
||||
'location' => 'main',
|
||||
'title' => t('Group'),
|
||||
'weight' => 60,
|
||||
],
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_farm_ui_theme_field_group_items().
|
||||
*/
|
||||
function farm_group_farm_ui_theme_field_group_items(string $entity_type, string $bundle) {
|
||||
if ($entity_type == 'log') {
|
||||
return [
|
||||
'group' => 'group',
|
||||
'is_group_assignment' => 'group',
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -60,11 +60,9 @@ class MapRenderEventSubscriber implements EventSubscriberInterface {
|
|||
*/
|
||||
public function onMapRender(MapRenderEvent $event) {
|
||||
|
||||
// Get the map ID.
|
||||
$map_id = $event->getmapType()->id();
|
||||
|
||||
// Add land type layers to dashboard map.
|
||||
if ($map_id == 'dashboard') {
|
||||
// If the "locations" behavior is added to the map, add layers for each
|
||||
// land type.
|
||||
if (in_array('locations', $event->getMapBehaviors())) {
|
||||
$layers = [];
|
||||
|
||||
// Define the parent group.
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- farm_product
|
||||
id: product
|
||||
label: Product
|
||||
description: ''
|
||||
workflow: asset_default
|
||||
new_revision: true
|
|
@ -0,0 +1,11 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- farm_product
|
||||
id: asset_product
|
||||
color: blue
|
||||
conditions:
|
||||
asset_type:
|
||||
- product
|
|
@ -0,0 +1,9 @@
|
|||
name: Product asset
|
||||
description: Adds an Product asset type.
|
||||
type: module
|
||||
package: farmOS Assets
|
||||
core_version_requirement: ^10
|
||||
dependencies:
|
||||
- farm:asset
|
||||
- farm:farm_entity
|
||||
- farm:farm_product_type
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_product\Plugin\Asset\AssetType;
|
||||
|
||||
use Drupal\farm_entity\Plugin\Asset\AssetType\FarmAssetType;
|
||||
|
||||
/**
|
||||
* Provides the product asset type.
|
||||
*
|
||||
* @AssetType(
|
||||
* id = "product",
|
||||
* label = @Translation("Product"),
|
||||
* )
|
||||
*/
|
||||
class Product extends FarmAssetType {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFieldDefinitions() {
|
||||
$fields = parent::buildFieldDefinitions();
|
||||
$field_info = [
|
||||
'product_type' => [
|
||||
'type' => 'entity_reference',
|
||||
'label' => $this->t('Product type'),
|
||||
'description' => $this->t("Enter the type of product."),
|
||||
'target_type' => 'taxonomy_term',
|
||||
'target_bundle' => 'product_type',
|
||||
'auto_create' => TRUE,
|
||||
'required' => TRUE,
|
||||
'weight' => [
|
||||
'form' => -90,
|
||||
'view' => -50,
|
||||
],
|
||||
],
|
||||
];
|
||||
foreach ($field_info as $name => $info) {
|
||||
$fields[$name] = $this->farmFieldFactory->bundleFieldDefinition($info);
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
|
@ -60,11 +60,9 @@ class MapRenderEventSubscriber implements EventSubscriberInterface {
|
|||
*/
|
||||
public function onMapRender(MapRenderEvent $event) {
|
||||
|
||||
// Get the map ID.
|
||||
$map_id = $event->getmapType()->id();
|
||||
|
||||
// Add structure type layers to dashboard map.
|
||||
if ($map_id == 'dashboard') {
|
||||
// If the "locations" behavior is added to the map, add layers for each
|
||||
// structure type.
|
||||
if (in_array('locations', $event->getMapBehaviors())) {
|
||||
$layers = [];
|
||||
|
||||
// Define the parent group.
|
||||
|
|
|
@ -148,7 +148,7 @@ class Asset extends RevisionableContentEntityBase implements AssetInterface {
|
|||
*/
|
||||
public function getBundleLabel() {
|
||||
/** @var \Drupal\asset\Entity\AssetTypeInterface $type */
|
||||
$type = \Drupal::entityTypeManager()
|
||||
$type = $this->entityTypeManager()
|
||||
->getStorage('asset_type')
|
||||
->load($this->bundle());
|
||||
return $type->label();
|
||||
|
|
|
@ -113,7 +113,7 @@ class AssetType extends ConfigEntityBundleBase implements AssetTypeInterface {
|
|||
|
||||
// If the asset type id changed, update all existing assets of that type.
|
||||
if ($update && $this->getOriginalId() != $this->id()) {
|
||||
$update_count = \Drupal::entityTypeManager()->getStorage('asset')->updateType($this->getOriginalId(), $this->id());
|
||||
$update_count = $this->entityTypeManager()->getStorage('asset')->updateType($this->getOriginalId(), $this->id());
|
||||
if ($update_count) {
|
||||
\Drupal::messenger()->addMessage(\Drupal::translation()->formatPlural($update_count,
|
||||
'Changed the asset type of 1 post from %old-type to %type.',
|
||||
|
@ -127,7 +127,7 @@ class AssetType extends ConfigEntityBundleBase implements AssetTypeInterface {
|
|||
if ($update) {
|
||||
// Clear the cached field definitions as some settings affect the field
|
||||
// definitions.
|
||||
\Drupal::entityTypeManager()->clearCachedDefinitions();
|
||||
$this->entityTypeManager()->clearCachedDefinitions();
|
||||
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class AssetCRUDTest extends AssetTestBase {
|
|||
];
|
||||
|
||||
$this->drupalGet('asset/add/default');
|
||||
$this->submitForm($edit, $this->t('Save'));
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$result = \Drupal::entityTypeManager()
|
||||
->getStorage('asset')
|
||||
|
@ -85,7 +85,7 @@ class AssetCRUDTest extends AssetTestBase {
|
|||
'name[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalGet($asset->toUrl('edit-form'));
|
||||
$this->submitForm($edit, $this->t('Save'));
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->pageTextContains($edit['name[0][value]']);
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ class AssetCRUDTest extends AssetTestBase {
|
|||
$asset_id = $asset->id();
|
||||
|
||||
$this->drupalGet($asset->toUrl('delete-form'));
|
||||
$this->submitForm([], $this->t('Delete'));
|
||||
$this->submitForm([], 'Delete');
|
||||
$this->assertSession()->responseContains($this->t('The @entity-type %label has been deleted.', [
|
||||
'@entity-type' => $asset->getEntityType()->getSingularLabel(),
|
||||
'%label' => $label,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
name: farmOS CSV
|
||||
description: Provides CSV features for farmOS.
|
||||
type: module
|
||||
package: farmOS
|
||||
core_version_requirement: ^10
|
|
@ -0,0 +1,19 @@
|
|||
services:
|
||||
farm_csv.normalizer.content_entity_normalizer:
|
||||
class: Drupal\farm_csv\Normalizer\ContentEntityNormalizer
|
||||
tags:
|
||||
- { name: normalizer, priority: 10 }
|
||||
arguments: ['@entity_type.manager', '@entity_type.repository', '@entity_field.manager']
|
||||
farm_csv.normalizer.text_long_field_item:
|
||||
class: Drupal\farm_csv\Normalizer\TextLongFieldItemNormalizer
|
||||
tags:
|
||||
- { name: normalizer, priority: 10 }
|
||||
farm_csv.normalizer.entity_reference_field_item:
|
||||
class: Drupal\farm_csv\Normalizer\EntityReferenceFieldItemNormalizer
|
||||
tags:
|
||||
- { name: normalizer, priority: 10 }
|
||||
arguments: ['@entity.repository']
|
||||
farm_csv.normalizer.timestamp_item:
|
||||
class: Drupal\farm_csv\Normalizer\TimestampItemNormalizer
|
||||
tags:
|
||||
- { name: normalizer, priority: 10 }
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_csv\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\serialization\Normalizer\ContentEntityNormalizer as CoreContentEntityNormalizer;
|
||||
|
||||
/**
|
||||
* Normalizes farmOS content entities for CSV exports.
|
||||
*/
|
||||
class ContentEntityNormalizer extends CoreContentEntityNormalizer {
|
||||
|
||||
/**
|
||||
* The supported format.
|
||||
*/
|
||||
const FORMAT = 'csv';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($entity, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
$data = parent::normalize($entity, $format, $context);
|
||||
|
||||
// If columns were explicitly included, remove others.
|
||||
if (!empty($context['include_columns'])) {
|
||||
foreach (array_keys($data) as $key) {
|
||||
if (!in_array($key, $context['include_columns'])) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, string $format = NULL, array $context = []): bool {
|
||||
return $data instanceof ContentEntityInterface && $format == static::FORMAT;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_csv\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItemInterface;
|
||||
use Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer as CoreEntityReferenceFieldItemNormalizer;
|
||||
|
||||
/**
|
||||
* Normalizes entity reference fields for farmOS CSV exports.
|
||||
*/
|
||||
class EntityReferenceFieldItemNormalizer extends CoreEntityReferenceFieldItemNormalizer {
|
||||
|
||||
/**
|
||||
* The supported format.
|
||||
*/
|
||||
const FORMAT = 'csv';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($field_item, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
|
||||
// Attempt to load the referenced entity.
|
||||
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
||||
if ($entity = $field_item->get('entity')->getValue()) {
|
||||
|
||||
// Return content entity labels, if desired.
|
||||
if ($entity instanceof ContentEntityInterface && isset($context['content_entity_labels']) && $context['content_entity_labels'] === TRUE) {
|
||||
return $entity->label();
|
||||
}
|
||||
|
||||
// Return config entity IDs, if desired.
|
||||
if ($entity instanceof ConfigEntityInterface && isset($context['config_entity_ids']) && $context['config_entity_ids'] === TRUE) {
|
||||
return $entity->id();
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, delegate to the parent method.
|
||||
return parent::normalize($field_item, $format, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, string $format = NULL, array $context = []): bool {
|
||||
return $data instanceof EntityReferenceItemInterface && $format == static::FORMAT;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_csv\Normalizer;
|
||||
|
||||
use Drupal\serialization\Normalizer\FieldItemNormalizer;
|
||||
use Drupal\text\Plugin\Field\FieldType\TextLongItem;
|
||||
|
||||
/**
|
||||
* Normalizes long text fields for farmOS CSV exports.
|
||||
*/
|
||||
class TextLongFieldItemNormalizer extends FieldItemNormalizer {
|
||||
|
||||
/**
|
||||
* The supported format.
|
||||
*/
|
||||
const FORMAT = 'csv';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($field_item, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
/** @var \Drupal\text\Plugin\Field\FieldType\TextLongItem $field_item */
|
||||
|
||||
// Return processed text, if desired.
|
||||
if (isset($context['processed_text']) && $context['processed_text'] === TRUE) {
|
||||
return $field_item->get('processed')->getValue();
|
||||
}
|
||||
|
||||
// Delegate to the parent method.
|
||||
return parent::normalize($field_item, $format, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, string $format = NULL, array $context = []): bool {
|
||||
return $data instanceof TextLongItem && $format == static::FORMAT;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_csv\Normalizer;
|
||||
|
||||
use Drupal\serialization\Normalizer\TimestampItemNormalizer as CoreTimestampItemNormalizer;
|
||||
|
||||
/**
|
||||
* Normalizes timestamp fields for farmOS CSV exports.
|
||||
*/
|
||||
class TimestampItemNormalizer extends CoreTimestampItemNormalizer {
|
||||
|
||||
/**
|
||||
* The supported format.
|
||||
*/
|
||||
const FORMAT = 'csv';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
$data = parent::normalize($object, $format, $context);
|
||||
|
||||
// Return the RFC3339 formatted date, if desired.
|
||||
if (isset($context['rfc3339_dates']) && $context['rfc3339_dates'] === TRUE) {
|
||||
return $data['value'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
|
@ -145,7 +145,7 @@ display:
|
|||
items_per_page: true
|
||||
items_per_page_label: Limit
|
||||
items_per_page_options: '1000,500,100,10,1'
|
||||
items_per_page_options_all: true
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
|
|
|
@ -134,7 +134,7 @@ class DataStream extends ContentEntityBase implements DataStreamInterface {
|
|||
*/
|
||||
public function getBundleLabel() {
|
||||
/** @var \Drupal\data_stream\Entity\DataStreamTypeInterface $type */
|
||||
$type = \Drupal::entityTypeManager()
|
||||
$type = $this->entityTypeManager()
|
||||
->getStorage('data_stream_type')
|
||||
->load($this->bundle());
|
||||
return $type->label();
|
||||
|
|
|
@ -16,6 +16,7 @@ function farm_entity_install() {
|
|||
'data_stream',
|
||||
'file',
|
||||
'log',
|
||||
'plan',
|
||||
'quantity',
|
||||
'taxonomy_term',
|
||||
'user',
|
||||
|
|
|
@ -46,7 +46,7 @@ function farm_entity_entity_type_build(array &$entity_types) {
|
|||
}
|
||||
|
||||
// Enable the use of bundle plugins on specific entity types.
|
||||
foreach (['asset', 'log', 'plan', 'quantity'] as $entity_type) {
|
||||
foreach (['asset', 'log', 'plan', 'plan_record', 'quantity'] as $entity_type) {
|
||||
if (!empty($entity_types[$entity_type])) {
|
||||
$entity_types[$entity_type]->set('bundle_plugin_type', $entity_type . '_type');
|
||||
$entity_types[$entity_type]->setHandlerClass('bundle_plugin', FarmEntityBundlePluginHandler::class);
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Post update hooks for the farm_entity module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enforce entity reference integrity on plan reference fields.
|
||||
*/
|
||||
function farm_entity_post_update_enforce_plan_eri(&$sandbox) {
|
||||
$config = \Drupal::configFactory()->getEditable('entity_reference_integrity_enforce.settings');
|
||||
$entity_types = $config->get('enabled_entity_type_ids');
|
||||
$entity_types['plan'] = 'plan';
|
||||
$config->set('enabled_entity_type_ids', $entity_types);
|
||||
$config->save();
|
||||
}
|
|
@ -12,6 +12,9 @@ services:
|
|||
plugin.manager.plan_type:
|
||||
class: Drupal\farm_entity\PlanTypeManager
|
||||
parent: default_plugin_manager
|
||||
plugin.manager.plan_record_type:
|
||||
class: Drupal\farm_entity\PlanRecordTypeManager
|
||||
parent: default_plugin_manager
|
||||
plugin.manager.quantity_type:
|
||||
class: Drupal\farm_entity\QuantityTypeManager
|
||||
parent: default_plugin_manager
|
||||
|
|
|
@ -39,7 +39,7 @@ function farm_entity_fields_asset_base_fields() {
|
|||
'type' => 'text_long',
|
||||
'label' => t('Notes'),
|
||||
'weight' => [
|
||||
'form' => 0,
|
||||
'form' => 95,
|
||||
'view' => 10,
|
||||
],
|
||||
],
|
||||
|
@ -87,7 +87,7 @@ function farm_entity_fields_log_base_fields() {
|
|||
'type' => 'text_long',
|
||||
'label' => t('Notes'),
|
||||
'weight' => [
|
||||
'form' => 0,
|
||||
'form' => 95,
|
||||
'view' => 10,
|
||||
],
|
||||
],
|
||||
|
@ -133,7 +133,7 @@ function farm_entity_fields_plan_base_fields() {
|
|||
'type' => 'text_long',
|
||||
'label' => t('Notes'),
|
||||
'weight' => [
|
||||
'form' => 0,
|
||||
'form' => 95,
|
||||
'view' => 10,
|
||||
],
|
||||
],
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_entity\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines the plan record relationship type plugin annotation object.
|
||||
*
|
||||
* Plugin namespace: Plugin\PlanRecord\PlanRecordType.
|
||||
*
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class PlanRecordType extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The plan record relationship type label.
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*/
|
||||
public $label;
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_entity;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
|
||||
/**
|
||||
* Manages discovery and instantiation of plan record relationship type plugins.
|
||||
*
|
||||
* @see \Drupal\farm_entity\Annotation\PlanType
|
||||
* @see plugin_api
|
||||
*/
|
||||
class PlanRecordTypeManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* Constructs a new PlanRecordTypeManager object.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* The cache backend.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct('Plugin/PlanRecord/PlanRecordType', $namespaces, $module_handler, 'Drupal\farm_entity\Plugin\PlanRecord\PlanRecordType\PlanRecordTypeInterface', 'Drupal\farm_entity\Annotation\PlanRecordType');
|
||||
|
||||
$this->alterInfo('plan_record_type_info');
|
||||
$this->setCacheBackend($cache_backend, 'plan_record_type_plugins');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processDefinition(&$definition, $plugin_id) {
|
||||
parent::processDefinition($definition, $plugin_id);
|
||||
|
||||
foreach (['id', 'label'] as $required_property) {
|
||||
if (empty($definition[$required_property])) {
|
||||
throw new PluginException(sprintf('The plan record relationship type %s must define the %s property.', $plugin_id, $required_property));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_entity\Plugin\PlanRecord\PlanRecordType;
|
||||
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Provides a farmOS plan record relationship type base class.
|
||||
*/
|
||||
class FarmPlanRecordType extends PlanRecordTypeBase {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_entity\Plugin\PlanRecord\PlanRecordType;
|
||||
|
||||
use Drupal\farm_entity\FarmEntityTypeBase;
|
||||
|
||||
/**
|
||||
* Provides the base plan record relationship type class.
|
||||
*/
|
||||
abstract class PlanRecordTypeBase extends FarmEntityTypeBase implements PlanRecordTypeInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->pluginDefinition['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFieldDefinitions() {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_entity\Plugin\PlanRecord\PlanRecordType;
|
||||
|
||||
use Drupal\entity\BundlePlugin\BundlePluginInterface;
|
||||
|
||||
/**
|
||||
* Defines the interface for plan record relationship types.
|
||||
*/
|
||||
interface PlanRecordTypeInterface extends BundlePluginInterface {
|
||||
|
||||
/**
|
||||
* Gets the plan record relationship type label.
|
||||
*
|
||||
* @return string
|
||||
* The plan record relationship type label.
|
||||
*/
|
||||
public function getLabel();
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
name: farmOS Export
|
||||
description: Features for exporting records.
|
||||
type: module
|
||||
package: farmOS
|
||||
core_version_requirement: ^10
|
|
@ -0,0 +1,11 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- asset
|
||||
- farm_export_csv
|
||||
id: asset_csv_action
|
||||
label: 'Export CSV'
|
||||
type: asset
|
||||
plugin: entity:csv_action:asset
|
||||
configuration: { }
|
|
@ -0,0 +1,11 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- farm_export_csv
|
||||
- log
|
||||
id: log_csv_action
|
||||
label: 'Export CSV'
|
||||
type: log
|
||||
plugin: entity:csv_action:log
|
||||
configuration: { }
|
|
@ -0,0 +1,8 @@
|
|||
name: farmOS Export CSV
|
||||
description: Provides a CSV export action for farmOS.
|
||||
type: module
|
||||
package: farmOS
|
||||
core_version_requirement: ^10
|
||||
dependencies:
|
||||
- farm:farm_export
|
||||
- farm:farm_csv
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* The farmOS Export CSV module.
|
||||
*/
|
||||
|
||||
use Drupal\farm_export_csv\Form\EntityCsvActionForm;
|
||||
use Drupal\farm_export_csv\Routing\EntityCsvActionRouteProvider;
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_build().
|
||||
*/
|
||||
function farm_export_csv_entity_type_build(array &$entity_types) {
|
||||
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
|
||||
|
||||
// Enable the entity CSV export action on assets and logs.
|
||||
foreach (['asset', 'log'] as $entity_type) {
|
||||
if (!empty($entity_types[$entity_type])) {
|
||||
$route_providers = $entity_types[$entity_type]->getRouteProviderClasses();
|
||||
$route_providers['csv'] = EntityCsvActionRouteProvider::class;
|
||||
$entity_types[$entity_type]->setHandlerClass('route_provider', $route_providers);
|
||||
$entity_types[$entity_type]->setLinkTemplate('csv-action-form', '/' . $entity_type . '/csv');
|
||||
$entity_types[$entity_type]->setFormClass('csv-action-form', EntityCsvActionForm::class);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,381 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_export_csv\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\File\FileSystemInterface;
|
||||
use Drupal\Core\File\FileUrlGeneratorInterface;
|
||||
use Drupal\Core\Form\BaseFormIdInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\file\FileRepositoryInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
* Provides an entity CSV action form.
|
||||
*
|
||||
* @see \Drupal\farm_export_csv\Plugin\Action\EntityCsv
|
||||
* @see \Drupal\Core\Entity\Form\DeleteMultipleForm
|
||||
*/
|
||||
class EntityCsvActionForm extends ConfirmFormBase implements BaseFormIdInterface {
|
||||
|
||||
/**
|
||||
* The tempstore factory.
|
||||
*
|
||||
* @var \Drupal\Core\TempStore\SharedTempStore
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The serializer service.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\SerializerInterface
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* The file system service.
|
||||
*
|
||||
* @var \Drupal\Core\File\FileSystemInterface
|
||||
*/
|
||||
protected $fileSystem;
|
||||
|
||||
/**
|
||||
* The default file scheme.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultFileScheme;
|
||||
|
||||
/**
|
||||
* The file repository service.
|
||||
*
|
||||
* @var \Drupal\file\FileRepositoryInterface
|
||||
*/
|
||||
protected $fileRepository;
|
||||
|
||||
/**
|
||||
* The file URL generator.
|
||||
*
|
||||
* @var \Drupal\Core\File\FileUrlGeneratorInterface
|
||||
*/
|
||||
protected $fileUrlGenerator;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* The entity type.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* The entities to export.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface[]
|
||||
*/
|
||||
protected $entities;
|
||||
|
||||
/**
|
||||
* Constructs an EntityCsvActionForm form object.
|
||||
*
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
|
||||
* The entity field manager.
|
||||
* @param \Symfony\Component\Serializer\SerializerInterface $serializer
|
||||
* The serializer service.
|
||||
* @param \Drupal\Core\File\FileSystemInterface $file_system
|
||||
* The file system service.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory service.
|
||||
* @param \Drupal\file\FileRepositoryInterface $file_repository
|
||||
* The file repository service.
|
||||
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
|
||||
* The file URL generator.
|
||||
* @param \Drupal\Core\Session\AccountInterface $user
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, SerializerInterface $serializer, FileSystemInterface $file_system, ConfigFactoryInterface $config_factory, FileRepositoryInterface $file_repository, FileUrlGeneratorInterface $file_url_generator, AccountInterface $user) {
|
||||
$this->tempStore = $temp_store_factory->get('entity_csv_confirm');
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->serializer = $serializer;
|
||||
$this->fileSystem = $file_system;
|
||||
$this->defaultFileScheme = $config_factory->get('system.file')->get('default_scheme') ?? 'public';
|
||||
$this->fileRepository = $file_repository;
|
||||
$this->fileUrlGenerator = $file_url_generator;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('tempstore.private'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('serializer'),
|
||||
$container->get('file_system'),
|
||||
$container->get('config.factory'),
|
||||
$container->get('file.repository'),
|
||||
$container->get('file_url_generator'),
|
||||
$container->get('current_user'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseFormId() {
|
||||
return 'entity_export_csv_action_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
// Get entity type ID from the route because ::buildForm has not yet been
|
||||
// called.
|
||||
$entity_type_id = $this->getRouteMatch()->getParameter('entity_type_id');
|
||||
return $entity_type_id . '_export_csv_action_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->formatPlural(count($this->entities), 'Export a CSV of @count @item?', 'Export a CSV of @count @items?', [
|
||||
'@item' => $this->entityType->getSingularLabel(),
|
||||
'@items' => $this->entityType->getPluralLabel(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
if ($this->entityType->hasLinkTemplate('collection')) {
|
||||
return new Url('entity.' . $this->entityType->id() . '.collection');
|
||||
}
|
||||
else {
|
||||
return new Url('<front>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Export');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
|
||||
|
||||
// If we don't have an entity type or list of entities, redirect.
|
||||
$this->entityType = $this->entityTypeManager->getDefinition($entity_type_id);
|
||||
$this->entities = $this->tempStore->get($this->user->id() . ':' . $entity_type_id);
|
||||
if (empty($entity_type_id) || empty($this->entities)) {
|
||||
return new RedirectResponse($this->getCancelUrl()
|
||||
->setAbsolute()
|
||||
->toString());
|
||||
}
|
||||
|
||||
// Make it clear that CSV exports are limited.
|
||||
$message = $this->t('Note: CSV exports do not include all @item data.', ['@item' => $this->entityType->getSingularLabel()]);
|
||||
$form['warning'] = [
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'strong',
|
||||
'#value' => $message,
|
||||
];
|
||||
|
||||
// Delegate to the parent method.
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
|
||||
// Filter out entities the user doesn't have access to.
|
||||
$inaccessible_entities = [];
|
||||
$accessible_entities = [];
|
||||
foreach ($this->entities as $entity) {
|
||||
if (!$entity->access('view', $this->currentUser())) {
|
||||
$inaccessible_entities[] = $entity;
|
||||
continue;
|
||||
}
|
||||
$accessible_entities[] = $entity;
|
||||
}
|
||||
|
||||
// Serialize the entities with the csv format.
|
||||
$context = [
|
||||
|
||||
// Define the columns to include.
|
||||
'include_columns' => $this->getIncludeColumns(),
|
||||
|
||||
// Return processed text from long text fields.
|
||||
'processed_text' => TRUE,
|
||||
|
||||
// Return content entity labels and config entity IDs.
|
||||
'content_entity_labels' => TRUE,
|
||||
'config_entity_ids' => TRUE,
|
||||
|
||||
// Return RFC3339 dates.
|
||||
'rfc3339_dates' => TRUE,
|
||||
];
|
||||
$output = $this->serializer->serialize($accessible_entities, 'csv', $context);
|
||||
|
||||
// Prepare the file directory.
|
||||
$directory = $this->defaultFileScheme . '://csv';
|
||||
$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);
|
||||
|
||||
// Create the file.
|
||||
$filename = 'csv_export-' . date('c') . '.csv';
|
||||
$destination = "$directory/$filename";
|
||||
try {
|
||||
$file = $this->fileRepository->writeData($output, $destination);
|
||||
}
|
||||
|
||||
// If file creation failed, bail with a warning.
|
||||
catch (\Exception $e) {
|
||||
$this->messenger()->addWarning($this->t('Could not create file.'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Make the file temporary.
|
||||
$file->status = 0;
|
||||
$file->save();
|
||||
|
||||
// Add warning message for inaccessible entities.
|
||||
if (!empty($inaccessible_entities)) {
|
||||
$inaccessible_count = count($inaccessible_entities);
|
||||
$this->messenger()->addWarning($this->formatPlural($inaccessible_count, 'Could not export @count @item because you do not have the necessary permissions.', 'Could not export @count @items because you do not have the necessary permissions.', [
|
||||
'@item' => $this->entityType->getSingularLabel(),
|
||||
'@items' => $this->entityType->getPluralLabel(),
|
||||
]));
|
||||
}
|
||||
|
||||
// Add confirmation message.
|
||||
if (count($accessible_entities)) {
|
||||
$this->messenger()->addStatus($this->formatPlural(count($accessible_entities), 'Exported @count @item.', 'Exported @count @items', [
|
||||
'@item' => $this->entityType->getSingularLabel(),
|
||||
'@items' => $this->entityType->getPluralLabel(),
|
||||
]));
|
||||
}
|
||||
|
||||
// Show a link to the file.
|
||||
$url = $this->fileUrlGenerator->generateAbsoluteString($file->getFileUri());
|
||||
$this->messenger()->addMessage($this->t('CSV file created: <a href=":url">%filename</a>', [
|
||||
':url' => $url,
|
||||
'%filename' => $file->label(),
|
||||
]));
|
||||
|
||||
$this->tempStore->delete($this->currentUser()->id() . ':' . $this->entityType->id());
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of columns to include in CSV exports.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of column names.
|
||||
*/
|
||||
protected function getIncludeColumns() {
|
||||
|
||||
// Start with ID and UUID.
|
||||
$columns = [
|
||||
'id',
|
||||
'uuid',
|
||||
];
|
||||
|
||||
// Define which field types are supported.
|
||||
$supported_field_types = [
|
||||
'boolean',
|
||||
'created',
|
||||
'changed',
|
||||
'entity_reference',
|
||||
'list_string',
|
||||
'state',
|
||||
'string',
|
||||
'text_long',
|
||||
'timestamp',
|
||||
];
|
||||
|
||||
// Add base field for supported field types.
|
||||
$base_field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($this->entityType->id());
|
||||
foreach ($base_field_definitions as $field_name => $field_definition) {
|
||||
if (!in_array($field_name, $columns) && in_array($field_definition->getType(), $supported_field_types)) {
|
||||
$columns[] = $field_name;
|
||||
}
|
||||
}
|
||||
|
||||
// Add bundle fields for supported field types.
|
||||
$bundles = $this->entityTypeManager->getStorage($this->entityType->getBundleEntityType())->loadMultiple();
|
||||
foreach ($bundles as $bundle) {
|
||||
if ($this->entityTypeManager->hasHandler($this->entityType->id(), 'bundle_plugin')) {
|
||||
$bundle_fields = $this->entityTypeManager->getHandler($this->entityType->id(), 'bundle_plugin')->getFieldDefinitions($bundle->id());
|
||||
foreach ($bundle_fields as $field_name => $field_definition) {
|
||||
if (!in_array($field_name, $columns) && in_array($field_definition->getType(), $supported_field_types)) {
|
||||
$columns[] = $field_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove revision and language columns.
|
||||
$remove_columns = [
|
||||
'default_langcode',
|
||||
'revision_translation_affected',
|
||||
'revision_created',
|
||||
'revision_user',
|
||||
];
|
||||
$columns = array_filter($columns, function ($name) use ($remove_columns) {
|
||||
return !in_array($name, $remove_columns);
|
||||
});
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_export_csv\Plugin\Action\Derivative;
|
||||
|
||||
use Drupal\Core\Action\Plugin\Action\Derivative\EntityActionDeriverBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
|
||||
/**
|
||||
* Provides an action deriver for the CSV action.
|
||||
*
|
||||
* @see \Drupal\farm_export_csv\Plugin\Action\EntityCsv
|
||||
*/
|
||||
class EntityCsvDeriver extends EntityActionDeriverBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
if (empty($this->derivatives)) {
|
||||
$definitions = [];
|
||||
foreach ($this->getApplicableEntityTypes() as $entity_type_id => $entity_type) {
|
||||
$definition = $base_plugin_definition;
|
||||
$definition['type'] = $entity_type_id;
|
||||
$definition['label'] = $this->t('Export @entity_type CSV', ['@entity_type' => $entity_type->getSingularLabel()]);
|
||||
$definition['confirm_form_route_name'] = 'entity.' . $entity_type->id() . '.csv_form';
|
||||
$definitions[$entity_type_id] = $definition;
|
||||
}
|
||||
$this->derivatives = $definitions;
|
||||
}
|
||||
|
||||
return parent::getDerivativeDefinitions($base_plugin_definition);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isApplicable(EntityTypeInterface $entity_type) {
|
||||
return in_array($entity_type->id(), ['log', 'asset']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_export_csv\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\Plugin\Action\EntityActionBase;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Action that exports a CSV file of entities.
|
||||
*
|
||||
* @Action(
|
||||
* id = "entity:csv_action",
|
||||
* action_label = @Translation("Export entity as CSV"),
|
||||
* deriver = "Drupal\farm_export_csv\Plugin\Action\Derivative\EntityCsvDeriver",
|
||||
* )
|
||||
*/
|
||||
class EntityCsv extends EntityActionBase {
|
||||
|
||||
/**
|
||||
* The tempstore object.
|
||||
*
|
||||
* @var \Drupal\Core\TempStore\SharedTempStore
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityCsv object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager service.
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* Current user.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
|
||||
$this->tempStore = $temp_store_factory->get('entity_csv_confirm');
|
||||
$this->currentUser = $current_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('tempstore.private'),
|
||||
$container->get('current_user'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeMultiple(array $entities) {
|
||||
/** @var \Drupal\Core\Entity\EntityInterface[] $entities */
|
||||
$this->tempStore->set($this->currentUser->id() . ':' . $this->getPluginDefinition()['type'], $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($object = NULL) {
|
||||
$this->executeMultiple([$object]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
return $object->access('view', $account, $return_as_object);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_export_csv\Routing;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Provides routes for the entity CSV export action.
|
||||
*/
|
||||
class EntityCsvActionRouteProvider implements EntityRouteProviderInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoutes(EntityTypeInterface $entity_type) {
|
||||
$collection = new RouteCollection();
|
||||
$entity_type_id = $entity_type->id();
|
||||
if ($route = $this->getEntityCsvFormRoute($entity_type)) {
|
||||
$collection->add("entity.$entity_type_id.csv_form", $route);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity CSV export form route.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route|null
|
||||
* The generated route, if available.
|
||||
*/
|
||||
protected function getEntityCsvFormRoute(EntityTypeInterface $entity_type) {
|
||||
if ($entity_type->hasLinkTemplate('csv-action-form')) {
|
||||
$route = new Route($entity_type->getLinkTemplate('csv-action-form'));
|
||||
$route->setDefault('_form', $entity_type->getFormClass('csv-action-form'));
|
||||
$route->setDefault('entity_type_id', $entity_type->id());
|
||||
$route->setRequirement('_user_is_logged_in', 'TRUE');
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@ status: true
|
|||
dependencies:
|
||||
module:
|
||||
- asset
|
||||
- farm_kml
|
||||
- farm_export_kml
|
||||
id: asset_kml_action
|
||||
label: 'Export KML'
|
||||
type: asset
|
|
@ -2,7 +2,7 @@ langcode: en
|
|||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- farm_kml
|
||||
- farm_export_kml
|
||||
- log
|
||||
id: log_kml_action
|
||||
label: 'Export KML'
|
|
@ -0,0 +1,8 @@
|
|||
name: farmOS Export KML
|
||||
description: Provides a KML export action for farmOS.
|
||||
type: module
|
||||
package: farmOS
|
||||
core_version_requirement: ^10
|
||||
dependencies:
|
||||
- farm:farm_export
|
||||
- farm:farm_kml
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_kml\Plugin\Action\Derivative;
|
||||
namespace Drupal\farm_export_kml\Plugin\Action\Derivative;
|
||||
|
||||
use Drupal\Core\Action\Plugin\Action\Derivative\EntityActionDeriverBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
|
@ -8,7 +8,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
|
|||
/**
|
||||
* Provides an action deriver for the KML action.
|
||||
*
|
||||
* @see \Drupal\farm_kml\Plugin\Action\EntityKml
|
||||
* @see \Drupal\farm_export_kml\Plugin\Action\EntityKml
|
||||
*/
|
||||
class EntityKmlDeriver extends EntityActionDeriverBase {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_kml\Plugin\Action;
|
||||
namespace Drupal\farm_export_kml\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\Plugin\Action\EntityActionBase;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
|
@ -18,7 +18,7 @@ use Symfony\Component\Serializer\SerializerInterface;
|
|||
* @Action(
|
||||
* id = "entity:kml_action",
|
||||
* action_label = @Translation("Export entity geometry as KML"),
|
||||
* deriver = "Drupal\farm_kml\Plugin\Action\Derivative\EntityKmlDeriver",
|
||||
* deriver = "Drupal\farm_export_kml\Plugin\Action\Derivative\EntityKmlDeriver",
|
||||
* )
|
||||
*/
|
||||
class EntityKml extends EntityActionBase {
|
|
@ -781,6 +781,14 @@ class FarmFieldFactory implements FarmFieldFactoryInterface {
|
|||
$field->setSetting('size', $options['size']);
|
||||
}
|
||||
|
||||
// Set the min/max constraints, if specified.
|
||||
if (isset($options['min'])) {
|
||||
$field->setSetting('min', $options['min']);
|
||||
}
|
||||
if (isset($options['max'])) {
|
||||
$field->setSetting('max', $options['max']);
|
||||
}
|
||||
|
||||
// Build form and view display settings.
|
||||
$field->setDisplayOptions('form', [
|
||||
'type' => 'number',
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Post update functions for farm_kml module.
|
||||
*/
|
||||
|
||||
use Drupal\system\Entity\Action;
|
||||
|
||||
/**
|
||||
* Move KML export actions to new farm_export_kml module.
|
||||
*/
|
||||
function farm_kml_post_update_move_kml_export_actions(&$sandbox = NULL) {
|
||||
|
||||
// Delete the existing KML export action config entities.
|
||||
$configs = Action::loadMultiple(['asset_kml_action', 'log_kml_action']);
|
||||
foreach ($configs as $config) {
|
||||
$config->delete();
|
||||
}
|
||||
|
||||
// Install the farm_export_kml module. This will recreate the actions.
|
||||
if (!\Drupal::service('module_handler')->moduleExists('farm_export_kml')) {
|
||||
\Drupal::service('module_installer')->install(['farm_export_kml']);
|
||||
}
|
||||
}
|
|
@ -141,7 +141,7 @@ class UserLoginTest extends FarmBrowserTestBase {
|
|||
$this->submitForm([
|
||||
'name' => $account->getEmail(),
|
||||
'pass' => $account->passRaw,
|
||||
], $this->t('Log in'));
|
||||
], 'Log in');
|
||||
if (isset($flood_trigger)) {
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
$this->assertSession()->fieldNotExists('pass');
|
||||
|
@ -187,7 +187,7 @@ class UserLoginTest extends FarmBrowserTestBase {
|
|||
$this->submitForm([
|
||||
'name' => $account->getEmail(),
|
||||
'pass' => $account->passRaw,
|
||||
], $this->t('Log in'));
|
||||
], 'Log in');
|
||||
|
||||
// @see ::drupalUserIsLoggedIn()
|
||||
$account->sessionId = $this->getSession()->getCookie(\Drupal::service('session_configuration')->getOptions(\Drupal::request())['name']);
|
||||
|
|
|
@ -72,6 +72,23 @@ class MapRenderEvent extends Event {
|
|||
return $this->mapType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter method for map behaviors.
|
||||
*
|
||||
* This returns a merged list of map behaviors from both the map type
|
||||
* configuration and the map element's #behaviors property.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of map behavior IDs.
|
||||
*/
|
||||
public function getMapBehaviors() {
|
||||
$behaviors = $this->getMapType()->getMapBehaviors();
|
||||
if (!empty($this->element['#behaviors'])) {
|
||||
$behaviors = array_merge($behaviors, $this->element['#behaviors']);
|
||||
}
|
||||
return $behaviors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add behavior to the map.
|
||||
*
|
||||
|
@ -92,8 +109,10 @@ class MapRenderEvent extends Event {
|
|||
/** @var \Drupal\farm_map\Entity\MapBehaviorInterface $behavior */
|
||||
$behavior = $this->entityTypeManager->getStorage('map_behavior')->load($behavior_name);
|
||||
|
||||
// Attach the library.
|
||||
$this->element['#attached']['library'][] = $behavior->getLibrary();
|
||||
// If the behavior has a library, attach it.
|
||||
if (!empty($behavior->getLibrary())) {
|
||||
$this->element['#attached']['library'][] = $behavior->getLibrary();
|
||||
}
|
||||
|
||||
// Add behavior settings if supplied.
|
||||
if (!empty($settings)) {
|
||||
|
|
|
@ -123,7 +123,7 @@ class MapBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
|||
return [
|
||||
'#type' => 'farm_map',
|
||||
'#map_type' => $this->configuration['map_type'] ?? 'default',
|
||||
'#behaviors' => array_keys($this->configuration['map_behaviors']) ?? [],
|
||||
'#behaviors' => $this->configuration['map_behaviors'] ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,20 @@ plan.type.*:
|
|||
type: boolean
|
||||
label: 'Create new revision'
|
||||
|
||||
plan.record.type.*:
|
||||
type: config_entity
|
||||
label: 'Plan record relationship type'
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
label: 'Machine-readable name'
|
||||
label:
|
||||
type: label
|
||||
label: 'Type'
|
||||
description:
|
||||
type: text
|
||||
label: 'Description'
|
||||
|
||||
condition.plugin.plan_type:
|
||||
type: condition.plugin
|
||||
mapping:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Post update hooks for the plan module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Install plan_record and plan_record_type entity types.
|
||||
*/
|
||||
function plan_post_update_install_plan_record(&$sandbox) {
|
||||
\Drupal::entityDefinitionUpdateManager()->installEntityType(
|
||||
\Drupal::entityTypeManager()->getDefinition('plan_record_type')
|
||||
);
|
||||
\Drupal::entityDefinitionUpdateManager()->installEntityType(
|
||||
\Drupal::entityTypeManager()->getDefinition('plan_record')
|
||||
);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\plan\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines plan_record access logic.
|
||||
*/
|
||||
class PlanRecordAccess extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
|
||||
// If a plan is referenced, access is based on access to the plan.
|
||||
/** @var \Drupal\plan\Entity\PlanRecordInterface $plan */
|
||||
if ($plan = $entity->getPlan()) {
|
||||
return AccessResult::allowedIf($plan->access($operation, $account));
|
||||
}
|
||||
|
||||
// Otherwise, delegate to the parent method.
|
||||
return parent::checkAccess($entity, $operation, $account);
|
||||
}
|
||||
|
||||
}
|
|
@ -148,7 +148,7 @@ class Plan extends RevisionableContentEntityBase implements PlanInterface {
|
|||
*/
|
||||
public function getBundleLabel() {
|
||||
/** @var \Drupal\plan\Entity\PlanTypeInterface $type */
|
||||
$type = \Drupal::entityTypeManager()
|
||||
$type = $this->entityTypeManager()
|
||||
->getStorage('plan_type')
|
||||
->load($this->bundle());
|
||||
return $type->label();
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\plan\Entity;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
|
||||
/**
|
||||
* Defines the Plan record relationship entity.
|
||||
*
|
||||
* This entity type can be used to create relationships between a plan and other
|
||||
* record(s) along with additional metadata fields to describe the relationship.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "plan_record",
|
||||
* label = @Translation("Plan record relationship"),
|
||||
* bundle_label = @Translation("Plan record relationship type"),
|
||||
* label_collection = @Translation("Plan record relationships"),
|
||||
* label_singular = @Translation("plan record relationship"),
|
||||
* label_plural = @Translation("plan record relationships"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count plan record relationship",
|
||||
* plural = "@count plan record relationships",
|
||||
* ),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\plan\Access\PlanRecordAccess",
|
||||
* "form" = {
|
||||
* "edit" = "Drupal\Core\Entity\ContentEntityForm",
|
||||
* },
|
||||
* "route_provider" = {
|
||||
* "default" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
|
||||
* },
|
||||
* },
|
||||
* base_table = "plan_record",
|
||||
* data_table = "plan_record_data",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "uuid" = "uuid",
|
||||
* "label" = "uuid",
|
||||
* "bundle" = "type",
|
||||
* },
|
||||
* bundle_entity_type = "plan_record_type",
|
||||
* common_reference_target = TRUE,
|
||||
* links = {
|
||||
* "edit-form" = "/plan/record/{plan_record}/edit",
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class PlanRecord extends ContentEntityBase implements PlanRecordInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBundleLabel() {
|
||||
/** @var \Drupal\plan\Entity\PlanRecordTypeInterface $type */
|
||||
$type = $this->entityTypeManager()
|
||||
->getStorage('plan_record_type')
|
||||
->load($this->bundle());
|
||||
return $type->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['plan'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Plan'))
|
||||
->setDescription(t('Associate this plan record relationship with a plan entity.'))
|
||||
->setTranslatable(FALSE)
|
||||
->setCardinality(1)
|
||||
->setSetting('target_type', 'plan')
|
||||
->setDisplayOptions('form', [
|
||||
'type' => 'entity_reference',
|
||||
'weight' => 12,
|
||||
])
|
||||
->setDisplayConfigurable('form', TRUE)
|
||||
->setDisplayConfigurable('view', TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPlan(): ?PlanInterface {
|
||||
return $this->get('plan')->first()?->entity;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\plan\Entity;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for defining plan record relationship entities.
|
||||
*/
|
||||
interface PlanRecordInterface extends ContentEntityInterface {
|
||||
|
||||
/**
|
||||
* Gets the label of the plan record relationship type.
|
||||
*
|
||||
* @return string
|
||||
* The label of the plan record relationship type.
|
||||
*/
|
||||
public function getBundleLabel();
|
||||
|
||||
/**
|
||||
* Returns the Plan entity the plan record is assigned to.
|
||||
*
|
||||
* @return \Drupal\plan\Entity\PlanInterface|null
|
||||
* The plant entity or NULL if not assigned.
|
||||
*/
|
||||
public function getPlan(): ?PlanInterface;
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\plan\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
|
||||
|
||||
/**
|
||||
* Defines the Plan record relationship type entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "plan_record_type",
|
||||
* label = @Translation("Plan record relationship type"),
|
||||
* label_collection = @Translation("Plan record relationship types"),
|
||||
* label_singular = @Translation("Plan record relationship type"),
|
||||
* label_plural = @Translation("plan record relationship types"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count plan record relationship type",
|
||||
* plural = "@count plan record relationship types",
|
||||
* ),
|
||||
* handlers = {
|
||||
* "access" = "\Drupal\entity\BundleEntityAccessControlHandler",
|
||||
* },
|
||||
* config_prefix = "record.type",
|
||||
* bundle_of = "plan_record",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label",
|
||||
* "uuid" = "uuid",
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "description",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class PlanRecordType extends ConfigEntityBundleBase implements PlanRecordTypeInterface {
|
||||
|
||||
/**
|
||||
* The Plan record relationship type ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The Plan record relationship type label.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* A brief description of this plan record relationship type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDescription($description) {
|
||||
return $this->set('description', $description);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\plan\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\EntityDescriptionInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for defining Plan record relationship type entities.
|
||||
*/
|
||||
interface PlanRecordTypeInterface extends ConfigEntityInterface, EntityDescriptionInterface {
|
||||
|
||||
}
|
|
@ -113,7 +113,7 @@ class PlanType extends ConfigEntityBundleBase implements PlanTypeInterface {
|
|||
|
||||
// If the plan type id changed, update all existing plans of that type.
|
||||
if ($update && $this->getOriginalId() != $this->id()) {
|
||||
$update_count = \Drupal::entityTypeManager()->getStorage('plan')->updateType($this->getOriginalId(), $this->id());
|
||||
$update_count = $this->entityTypeManager()->getStorage('plan')->updateType($this->getOriginalId(), $this->id());
|
||||
if ($update_count) {
|
||||
\Drupal::messenger()->addMessage(\Drupal::translation()->formatPlural($update_count,
|
||||
'Changed the plan type of 1 post from %old-type to %type.',
|
||||
|
@ -127,7 +127,7 @@ class PlanType extends ConfigEntityBundleBase implements PlanTypeInterface {
|
|||
if ($update) {
|
||||
// Clear the cached field definitions as some settings affect the field
|
||||
// definitions.
|
||||
\Drupal::entityTypeManager()->clearCachedDefinitions();
|
||||
$this->entityTypeManager()->clearCachedDefinitions();
|
||||
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class PlanCRUDTest extends PlanTestBase {
|
|||
];
|
||||
|
||||
$this->drupalGet('plan/add/default');
|
||||
$this->submitForm($edit, $this->t('Save'));
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$result = \Drupal::entityTypeManager()
|
||||
->getStorage('plan')
|
||||
|
@ -85,7 +85,7 @@ class PlanCRUDTest extends PlanTestBase {
|
|||
'name[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalGet($plan->toUrl('edit-form'));
|
||||
$this->submitForm($edit, $this->t('Save'));
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
$this->assertSession()->pageTextContains($edit['name[0][value]']);
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class PlanCRUDTest extends PlanTestBase {
|
|||
$plan_id = $plan->id();
|
||||
|
||||
$this->drupalGet($plan->toUrl('delete-form'));
|
||||
$this->submitForm([], $this->t('Delete'));
|
||||
$this->submitForm([], 'Delete');
|
||||
$this->assertSession()->responseContains($this->t('The @entity-type %label has been deleted.', [
|
||||
'@entity-type' => $plan->getEntityType()->getSingularLabel(),
|
||||
'%label' => $label,
|
||||
|
|
|
@ -118,7 +118,7 @@ class Quantity extends RevisionableContentEntityBase implements QuantityInterfac
|
|||
*/
|
||||
public function getBundleLabel() {
|
||||
/** @var \Drupal\quantity\Entity\QuantityTypeInterface $type */
|
||||
$type = \Drupal::entityTypeManager()
|
||||
$type = $this->entityTypeManager()
|
||||
->getStorage('quantity_type')
|
||||
->load($this->bundle());
|
||||
return $type->label();
|
||||
|
|
|
@ -8,5 +8,6 @@ dependencies:
|
|||
- drupal:taxonomy
|
||||
- farm:asset
|
||||
- farm:farm_log_quantity
|
||||
- farm:farm_setup
|
||||
- farm:quantity
|
||||
- log:log
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
farm_quick.add_page:
|
||||
title: 'Add quick form'
|
||||
route_name: farm_quick.add_page
|
||||
appears_on:
|
||||
- entity.quick_form.collection
|
|
@ -1,3 +1,8 @@
|
|||
farm.quick:
|
||||
class: Drupal\Core\Menu\MenuLinkDefault
|
||||
deriver: Drupal\farm_quick\Plugin\Derivative\QuickFormMenuLink
|
||||
farm.quick_setup:
|
||||
title: Quick Forms
|
||||
description: Quick forms make it easy to record common activities.
|
||||
parent: farm.setup
|
||||
route_name: entity.quick_form.collection
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
farm_quick:
|
||||
config_permissions:
|
||||
- create quick_form
|
||||
- update quick_form
|
||||
- administer quick_form
|
||||
default_permissions:
|
||||
- view quick_form
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
* The farmOS Quick Form module.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
|
@ -23,11 +25,16 @@ function farm_quick_help($route_name, RouteMatchInterface $route_match) {
|
|||
if (strpos($route_name, 'farm.quick.') === 0) {
|
||||
$quick_form_id = $route_match->getParameter('id');
|
||||
if ($route_name == 'farm.quick.' . $quick_form_id) {
|
||||
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface $quick_form */
|
||||
$quick_form = \Drupal::service('quick_form.instance_manager')->getInstance($quick_form_id);
|
||||
$help_text = $quick_form->getHelpText();
|
||||
if (!empty($help_text)) {
|
||||
$output .= '<p>' . $help_text . '</p>';
|
||||
}
|
||||
$output = [
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'p',
|
||||
'#value' => Html::escape($quick_form->getHelpText()),
|
||||
'#cache' => [
|
||||
'tags' => $quick_form->getCacheTags(),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,3 +64,42 @@ function farm_quick_farm_entity_bundle_field_info(EntityTypeInterface $entity_ty
|
|||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*/
|
||||
function farm_quick_form_alter(&$form, FormStateInterface $form_state, $form_id) {
|
||||
|
||||
// Only alter views_form_ forms.
|
||||
if (!str_starts_with($form_id, 'views_form_')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$target = NULL;
|
||||
if (isset($form['header']['asset_bulk_form']['action'])) {
|
||||
$target = 'asset_bulk_form';
|
||||
}
|
||||
if (isset($form['header']['log_bulk_form']['action'])) {
|
||||
$target = 'log_bulk_form';
|
||||
}
|
||||
|
||||
// Alter action options for the target entity type bulk form.
|
||||
if ($target) {
|
||||
|
||||
// Check for disabled quick forms.
|
||||
$disabled_quick_forms = \Drupal::entityTypeManager()->getStorage('quick_form')->getQuery()
|
||||
->accessCheck(TRUE)
|
||||
->condition('status', FALSE)
|
||||
->execute();
|
||||
if (empty($disabled_quick_forms)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove system actions that end with quick_* for a disabled quick form.
|
||||
foreach (array_keys($form['header'][$target]['action']['#options']) as $option_id) {
|
||||
if ((preg_match("/quick_(.*)/", $option_id, $matches)) && in_array($matches[1], $disabled_quick_forms)) {
|
||||
unset($form['header'][$target]['action']['#options'][$option_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,23 @@ farm.quick:
|
|||
requirements:
|
||||
_permission: 'view quick_form'
|
||||
|
||||
farm_quick.configure:
|
||||
path: /quick/{quick_form}/configure
|
||||
farm_quick.add_page:
|
||||
path: 'setup/quick/add'
|
||||
defaults:
|
||||
_entity_form: quick_form.configure
|
||||
_title_callback: \Drupal\farm_quick\Form\ConfigureQuickForm::getTitle
|
||||
_controller: \Drupal\farm_quick\Controller\QuickFormAddPage::addPage
|
||||
_title: 'Add quick form'
|
||||
requirements:
|
||||
_custom_access: \Drupal\farm_quick\Form\ConfigureQuickForm::access
|
||||
_permission: 'create quick_form'
|
||||
|
||||
farm_quick.add_form:
|
||||
path: '/setup/quick/add/{plugin}'
|
||||
defaults:
|
||||
_entity_form: quick_form.add
|
||||
requirements:
|
||||
_permission: 'create quick_form'
|
||||
options:
|
||||
parameters:
|
||||
quick_form:
|
||||
plugin:
|
||||
type: string
|
||||
|
||||
route_callbacks:
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_quick\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\farm_quick\QuickFormPluginManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Page that renders links to create instances of quick form plugins.
|
||||
*/
|
||||
class QuickFormAddPage extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The quick form plugin manager.
|
||||
*
|
||||
* @var \Drupal\farm_quick\QuickFormPluginManager
|
||||
*/
|
||||
protected $quickFormPluginManager;
|
||||
|
||||
/**
|
||||
* Constructs a new QuickFormAddPage object.
|
||||
*/
|
||||
public function __construct(QuickFormPluginManager $quick_form_plugin_manager) {
|
||||
$this->quickFormPluginManager = $quick_form_plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.quick_form'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add quick form page callback.
|
||||
*
|
||||
* @return array
|
||||
* Render array.
|
||||
*/
|
||||
public function addPage(): array {
|
||||
|
||||
$render = [
|
||||
'#theme' => 'entity_add_list',
|
||||
'#bundles' => [],
|
||||
'#cache' => [
|
||||
'tags' => $this->quickFormPluginManager->getCacheTags(),
|
||||
],
|
||||
];
|
||||
|
||||
// Filter to configurable quick form plugins.
|
||||
$plugins = array_filter($this->quickFormPluginManager->getDefinitions(), function (array $plugin) {
|
||||
if (($instance = $this->quickFormPluginManager->createInstance($plugin['id'])) && $instance->isConfigurable()) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
});
|
||||
|
||||
if (empty($plugins)) {
|
||||
$render['#add_bundle_message'] = $this->t('No quick forms are available. Enable a module that provides quick forms.');
|
||||
}
|
||||
|
||||
// Add link for each configurable plugin.
|
||||
foreach ($plugins as $plugin_id => $plugin) {
|
||||
$render['#bundles'][$plugin_id] = [
|
||||
'label' => Html::escape($plugin['label']),
|
||||
'description' => Html::escape($plugin['description']) ?? '',
|
||||
'add_link' => Link::createFromRoute($plugin['label'], 'farm_quick.add_form', ['plugin' => $plugin_id]),
|
||||
];
|
||||
}
|
||||
|
||||
return $render;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace Drupal\farm_quick\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Render\Markup;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
|
||||
|
@ -63,8 +65,15 @@ class QuickFormController extends ControllerBase {
|
|||
$url = Url::fromRoute('farm.quick.' . $id);
|
||||
if ($url->access()) {
|
||||
$items[] = [
|
||||
'title' => $quick_form->getLabel(),
|
||||
'description' => $quick_form->getDescription(),
|
||||
// Wrap the title in Markup::create() because the template preprocess
|
||||
// function for admin_block_content uses Link::fromTextAndUrl(), which
|
||||
// sanitizes strings automatically. This avoids double-sanitization,
|
||||
// but also ensures we are sanitizing consistently in this code, in
|
||||
// case anything changes later.
|
||||
// @see template_preprocess_admin_block_content()
|
||||
// @see \Drupal\Core\Link::fromTextAndUrl()
|
||||
'title' => Markup::create(Html::escape($quick_form->getLabel())),
|
||||
'description' => Html::escape($quick_form->getDescription()),
|
||||
'url' => $url,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -23,13 +23,27 @@ use Drupal\farm_quick\QuickFormPluginCollection;
|
|||
* handlers = {
|
||||
* "access" = "\Drupal\entity\EntityAccessControlHandler",
|
||||
* "permission_provider" = "\Drupal\entity\EntityPermissionProvider",
|
||||
* "list_builder" = "Drupal\farm_quick\QuickFormListBuilder",
|
||||
* "form" = {
|
||||
* "add" = "Drupal\farm_quick\Form\QuickFormEntityForm",
|
||||
* "edit" = "Drupal\farm_quick\Form\QuickFormEntityForm",
|
||||
* "configure" = "Drupal\farm_quick\Form\ConfigureQuickForm",
|
||||
* "delete" = "\Drupal\Core\Entity\EntityDeleteForm",
|
||||
* },
|
||||
* "route_provider" = {
|
||||
* "default" = "Drupal\entity\Routing\DefaultHtmlRouteProvider",
|
||||
* },
|
||||
* },
|
||||
* admin_permission = "administer quick_form",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "status" = "status"
|
||||
* "status" = "status",
|
||||
* "label" = "label",
|
||||
* },
|
||||
* links = {
|
||||
* "edit-form" = "/setup/quick/{quick_form}/edit",
|
||||
* "delete-form" = "/setup/quick/{quick_form}/delete",
|
||||
* "collection" = "/setup/quick"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_quick\Form;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\SubformState;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\farm_quick\Plugin\QuickForm\ConfigurableQuickFormInterface;
|
||||
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* Form that renders quick form configuration forms.
|
||||
*
|
||||
* @ingroup farm
|
||||
*/
|
||||
class ConfigureQuickForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The entity being used by this form.
|
||||
*
|
||||
* @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The quick form instance manager.
|
||||
*
|
||||
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
|
||||
*/
|
||||
protected $quickFormInstanceManager;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param \Drupal\farm_quick\QuickFormInstanceManagerInterface $quick_form_instance_manager
|
||||
* The quick form instance manager.
|
||||
*/
|
||||
public function __construct(QuickFormInstanceManagerInterface $quick_form_instance_manager) {
|
||||
$this->quickFormInstanceManager = $quick_form_instance_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('quick_form.instance_manager'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the quick form.
|
||||
*
|
||||
* @param string $quick_form
|
||||
* The quick form ID.
|
||||
*
|
||||
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* Quick form title.
|
||||
*/
|
||||
public function getTitle(string $quick_form) {
|
||||
$quick_form_title = NULL;
|
||||
if ($quick_form = $this->getQuickFormInstance($quick_form)) {
|
||||
$quick_form_title = $quick_form->getLabel();
|
||||
}
|
||||
return $this->t('Configure @quick_form', ['@quick_form' => $quick_form_title]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access for configuration of a specific quick form.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
* @param string|null $quick_form
|
||||
* The quick form ID.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(AccountInterface $account, string $quick_form = NULL) {
|
||||
|
||||
// Get a quick form config entity.
|
||||
if ($quick_form !== NULL) {
|
||||
$quick_form = $this->getQuickFormInstance($quick_form);
|
||||
}
|
||||
|
||||
// Raise 404 if no quick form exists. This is the case with a quick form
|
||||
// ID that is not a valid quick form plugin ID.
|
||||
if ($quick_form === NULL) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
// Deny access if the quick form plugin is not configurable.
|
||||
if (!$quick_form->getPlugin() instanceof ConfigurableQuickFormInterface) {
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
// Check the update quick_form permission.
|
||||
$configure_form_access = AccessResult::allowedIfHasPermissions($account, ['update quick_form']);
|
||||
return $quick_form->getPlugin()->access($account)->andIf($configure_form_access);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
$form['settings'] = [
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
$form['settings'] = $this->entity->getPlugin()->buildConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
$this->entity->getPlugin()->validateConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
$this->entity->getPlugin()->submitConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
|
||||
$entity = NULL;
|
||||
if ($route_match->getRawParameter($entity_type_id) !== NULL) {
|
||||
$entity = $this->getQuickFormInstance($route_match->getParameter($entity_type_id));
|
||||
}
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a quick form instance.
|
||||
*
|
||||
* @param string $quick_form_id
|
||||
* The quick form ID.
|
||||
*
|
||||
* @return \Drupal\farm_quick\Entity\QuickFormInstanceInterface|null
|
||||
* The quick form instance or NULL if does not exist.
|
||||
*/
|
||||
protected function getQuickFormInstance(string $quick_form_id) {
|
||||
return $this->quickFormInstanceManager->getInstance($quick_form_id);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_quick\Form;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\SubformState;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\farm_quick\Entity\QuickFormInstance;
|
||||
use Drupal\farm_quick\QuickFormPluginManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Form that renders quick form configuration forms.
|
||||
*/
|
||||
class QuickFormEntityForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The entity being used by this form.
|
||||
*
|
||||
* @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The quick form plugin manager.
|
||||
*
|
||||
* @var \Drupal\farm_quick\QuickFormPluginManager
|
||||
*/
|
||||
protected $quickFormPluginManager;
|
||||
|
||||
/**
|
||||
* Constructs a new QuickFormEntityForm object.
|
||||
*
|
||||
* @param \Drupal\farm_quick\QuickFormPluginManager $quick_form_plugin_manager
|
||||
* The quick form plugin manager.
|
||||
*/
|
||||
public function __construct(QuickFormPluginManager $quick_form_plugin_manager) {
|
||||
$this->quickFormPluginManager = $quick_form_plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.quick_form'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state, string $plugin = NULL) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
// Add tabs if the quick form plugin is configurable.
|
||||
$tab_group = NULL;
|
||||
if ($this->entity->getPlugin()->isConfigurable()) {
|
||||
$form['tabs'] = [
|
||||
'#type' => 'vertical_tabs',
|
||||
];
|
||||
$form['quick_form'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Quick form'),
|
||||
'#group' => 'tabs',
|
||||
];
|
||||
$tab_group = 'quick_form';
|
||||
|
||||
// Render the plugin form in settings tab.
|
||||
$form['settings_tab'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => Html::escape($this->entity->getPlugin()->getLabel()),
|
||||
'#group' => 'tabs',
|
||||
'#weight' => 50,
|
||||
];
|
||||
$form['settings'] = [
|
||||
'#tree' => TRUE,
|
||||
'#type' => 'container',
|
||||
'#group' => 'settings_tab',
|
||||
];
|
||||
$form['settings'] = $this->entity->getPlugin()->buildConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
|
||||
}
|
||||
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#required' => TRUE,
|
||||
'#group' => $tab_group,
|
||||
];
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'machine_name',
|
||||
'#machine_name' => [
|
||||
'exists' => '\Drupal\farm_quick\Entity\QuickFormInstance::load',
|
||||
],
|
||||
'#disabled' => !$this->entity->isNew() || $this->getRequest()->get('override'),
|
||||
'#group' => $tab_group,
|
||||
];
|
||||
|
||||
// Provide default label and ID for existing config entities
|
||||
// or if the override parameter is set.
|
||||
if (!$this->entity->isNew() || $this->getRequest()->get('override')) {
|
||||
$form['label']['#default_value'] = $this->entity->label();
|
||||
$form['id']['#default_value'] = $this->entity->id();
|
||||
}
|
||||
|
||||
// Adjust form title.
|
||||
if ($this->entity->isNew()) {
|
||||
$form['#title'] = $this->t('Add quick form: @label', ['@label' => $this->entity->getPlugin()->getLabel()]);
|
||||
if ($this->getRequest()->get('override')) {
|
||||
$form['#title'] = $this->t('Override quick form: @label', ['@label' => $this->entity->getPlugin()->getLabel()]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$form['#title'] = $this->t('Edit quick form: @label', ['@label' => $this->entity->label()]);
|
||||
}
|
||||
|
||||
$form['description'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Description'),
|
||||
'#description' => $this->t('A brief description of this quick form.'),
|
||||
'#default_value' => $this->entity->getDescription(),
|
||||
'#group' => $tab_group,
|
||||
];
|
||||
|
||||
$form['status'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Enabled'),
|
||||
'#description' => $this->t('Enable the quick form.'),
|
||||
'#default_value' => $this->entity->status(),
|
||||
'#group' => $tab_group,
|
||||
];
|
||||
|
||||
$form['helpText'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Help Text'),
|
||||
'#description' => $this->t('Help text to display for the quick form.'),
|
||||
'#default_value' => $this->entity->getHelpText(),
|
||||
'#group' => $tab_group,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
// Validate plugin form.
|
||||
if ($this->entity->getPlugin()->isConfigurable()) {
|
||||
$this->entity->getPlugin()->validateConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
// Submit plugin form.
|
||||
if ($this->entity->getPlugin()->isConfigurable()) {
|
||||
$this->entity->getPlugin()->submitConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$status = parent::save($form, $form_state);
|
||||
$entity_type_label = $this->entity->getEntityType()->getSingularLabel();
|
||||
$this->messenger()->addMessage($this->t('Saved @entity_type_label: %label', ['@entity_type_label' => $entity_type_label, '%label' => $this->entity->label()]));
|
||||
$form_state->setRedirect('entity.quick_form.collection');
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
|
||||
|
||||
// Get existing quick form entity from route parameter.
|
||||
if ($route_match->getRawParameter($entity_type_id) !== NULL) {
|
||||
$entity = $route_match->getParameter($entity_type_id);
|
||||
}
|
||||
// Else create a new quick form entity, the plugin must be specified.
|
||||
else {
|
||||
if (($plugin = $route_match->getRawParameter('plugin')) && $this->quickFormPluginManager->hasDefinition($plugin)) {
|
||||
$entity = QuickFormInstance::create(['plugin' => $plugin]);
|
||||
if ($this->getRequest()->get('override')) {
|
||||
$entity->set('id', $plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($entity)) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\farm_quick\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
@ -59,9 +60,16 @@ class QuickFormMenuLink extends DeriverBase implements ContainerDeriverInterface
|
|||
|
||||
// Add a link for each quick form.
|
||||
foreach ($quick_forms as $id => $quick_form) {
|
||||
|
||||
// Skip disabled quick forms.
|
||||
if (!$quick_form->status()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create link.
|
||||
$route_id = 'farm.quick.' . $id;
|
||||
$links[$route_id] = [
|
||||
'title' => $quick_form->getLabel(),
|
||||
'title' => Html::escape($quick_form->getLabel()),
|
||||
'parent' => 'farm.quick:farm.quick',
|
||||
'route_name' => $route_id,
|
||||
] + $base_plugin_definition;
|
||||
|
|
|
@ -59,19 +59,6 @@ class QuickFormTaskLink extends DeriverBase implements ContainerDeriverInterface
|
|||
'base_route' => $route_name,
|
||||
'weight' => 0,
|
||||
] + $base_plugin_definition;
|
||||
|
||||
// If the quick form is configurable, add a link to the config form.
|
||||
if ($quick_form->getPlugin()->isConfigurable()) {
|
||||
$links["farm.quick.$id.configure"] = [
|
||||
'title' => $this->t('Configure'),
|
||||
'route_name' => 'farm_quick.configure',
|
||||
'route_parameters' => [
|
||||
'quick_form' => $id,
|
||||
],
|
||||
'base_route' => $route_name,
|
||||
'weight' => 100,
|
||||
] + $base_plugin_definition;
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
|
|
|
@ -60,7 +60,7 @@ class QuickFormInstanceManager implements QuickFormInstanceManagerInterface {
|
|||
// Load quick form instance configuration entities for this plugin.
|
||||
// Exclude disabled quick forms.
|
||||
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface[] $entities */
|
||||
$entities = $this->entityTypeManager->getStorage('quick_form')->loadByProperties(['plugin' => $plugin['id'], 'status' => TRUE]);
|
||||
$entities = $this->entityTypeManager->getStorage('quick_form')->loadByProperties(['plugin' => $plugin['id']]);
|
||||
foreach ($entities as $entity) {
|
||||
$entity->getPlugin()->setQuickId($entity->id());
|
||||
$instances[$entity->id()] = $entity;
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\farm_quick;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a listing of template entities.
|
||||
*/
|
||||
class QuickFormListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* The quick form instance manager.
|
||||
*
|
||||
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
|
||||
*/
|
||||
protected $quickFormInstanceManager;
|
||||
|
||||
/**
|
||||
* Constructs a new QuickFormListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\farm_quick\QuickFormInstanceManagerInterface $quick_form_instance_manager
|
||||
* The quick form instance manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, QuickFormInstanceManagerInterface $quick_form_instance_manager) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
$this->quickFormInstanceManager = $quick_form_instance_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity_type.manager')->getStorage($entity_type->id()),
|
||||
$container->get('quick_form.instance_manager'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load() {
|
||||
return $this->quickFormInstanceManager->getInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
|
||||
$render['table'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $this->buildHeader(),
|
||||
'#caption' => $this->t('Configured quick forms'),
|
||||
'#rows' => [],
|
||||
'#empty' => $this->t('There are no configured @label.', ['@label' => $this->entityType->getPluralLabel()]),
|
||||
'#cache' => [
|
||||
'contexts' => $this->entityType->getListCacheContexts(),
|
||||
'tags' => $this->entityType->getListCacheTags(),
|
||||
],
|
||||
];
|
||||
|
||||
$render['default'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $this->buildHeader(),
|
||||
'#caption' => $this->t('Default quick forms'),
|
||||
'#rows' => [],
|
||||
'#empty' => $this->t('There are no default @label.', ['@label' => $this->entityType->getPluralLabel()]),
|
||||
];
|
||||
|
||||
// Load all quick form instances into proper table.
|
||||
$quick_form_instances = $this->load();
|
||||
foreach ($quick_form_instances as $entity) {
|
||||
$target = $entity->isNew() ? 'default' : 'table';
|
||||
if ($row = $this->buildRow($entity)) {
|
||||
$render[$target][$entity->id()] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $render;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['enabled'] = $this->t('Enabled');
|
||||
$header['type'] = $this->t('Plugin');
|
||||
$header['label'] = $this->t('Label');
|
||||
$header['id'] = $this->t('ID');
|
||||
$header['description'] = $this->t('Description');
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface $quick_form */
|
||||
$quick_form = $entity;
|
||||
$row['enabled'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#checked' => $quick_form->status(),
|
||||
'#attributes' => [
|
||||
'disabled' => 'disabled',
|
||||
],
|
||||
];
|
||||
$row['type'] = [
|
||||
'#plain_text' => $quick_form->getPlugin()->getLabel(),
|
||||
];
|
||||
$row['label'] = [
|
||||
'#plain_text' => $quick_form->getLabel(),
|
||||
];
|
||||
$row['id'] = [
|
||||
'#plain_text' => $quick_form->id(),
|
||||
];
|
||||
$row['description'] = [
|
||||
'#plain_text' => $quick_form->getDescription(),
|
||||
];
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
|
||||
// Override operations for default quick form instances.
|
||||
if ($entity->isNew()) {
|
||||
|
||||
// Remove edit operation.
|
||||
unset($operations['edit']);
|
||||
|
||||
// Add override operation.
|
||||
$operations['override'] = [
|
||||
'title' => $this->t('Override'),
|
||||
'weight' => 0,
|
||||
'url' => $this->ensureDestination(Url::fromRoute('farm_quick.add_form', ['plugin' => $entity->getPluginId()], ['query' => ['override' => TRUE]])),
|
||||
];
|
||||
}
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
}
|
|
@ -52,6 +52,11 @@ class QuickFormRoutes implements ContainerInjectionInterface {
|
|||
$quick_forms = $this->quickFormInstanceManager->getInstances();
|
||||
foreach ($quick_forms as $id => $quick_form) {
|
||||
|
||||
// Skip quick forms that are disabled.
|
||||
if (!$quick_form->status()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build a route for the quick form.
|
||||
$route = new Route(
|
||||
"/quick/$id",
|
||||
|
|
|
@ -91,21 +91,10 @@ class QuickFormTest extends FarmBrowserTestBase {
|
|||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->responseContains('value="100"');
|
||||
|
||||
// Go to the test configuration form and confirm that access is denied.
|
||||
$this->drupalGet('quick/configurable_test/configure');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Create and login a test user with permission to create test logs and
|
||||
// permission to update quick forms.
|
||||
$user = $this->createUser(['view quick_form', 'create test log', 'update quick_form']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Go to the default configurable_test quick form and confirm that the
|
||||
// default value field is visible and the default value is 100.
|
||||
$this->drupalGet('quick/configurable_test/configure');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($this->t('Default value'));
|
||||
$this->assertSession()->responseContains('value="100"');
|
||||
// Attempt to load the edit form for the unsaved configurable_test quick
|
||||
// form and confirm 404 not found.
|
||||
$this->drupalGet('setup/quick/foo/configurable_test');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Go to the configurable_test2 quick form and confirm access is granted and
|
||||
// the default value is 500.
|
||||
|
@ -113,9 +102,19 @@ class QuickFormTest extends FarmBrowserTestBase {
|
|||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->responseContains('value="500"');
|
||||
|
||||
// Attempt to load the edit form for saved configurable_test2 quick
|
||||
// form and confirm 403.
|
||||
$this->drupalGet('setup/quick/configurable_test2/edit');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Create and login a test user with permission to create test logs and
|
||||
// permission to update quick forms.
|
||||
$user = $this->createUser(['view quick_form', 'create test log', 'update quick_form']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Go to the configurable_test2 quick form and confirm that the default
|
||||
// value field is visible and the default value is 500.
|
||||
$this->drupalGet('quick/configurable_test2/configure');
|
||||
$this->drupalGet('setup/quick/configurable_test2/edit');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($this->t('Default value'));
|
||||
$this->assertSession()->responseContains('value="500"');
|
||||
|
@ -127,12 +126,12 @@ class QuickFormTest extends FarmBrowserTestBase {
|
|||
$config_entity->save();
|
||||
$this->drupalGet('quick/configurable_test2');
|
||||
$this->assertSession()->responseContains('value="600"');
|
||||
$this->drupalGet('quick/configurable_test2/configure');
|
||||
$this->drupalGet('setup/quick/configurable_test2/edit');
|
||||
$this->assertSession()->responseContains('value="600"');
|
||||
|
||||
// Attempt to load a configuration form for a non-existent quick form and
|
||||
// Attempt to load an edit form for a non-existent quick form and
|
||||
// confirm 404 not found.
|
||||
$this->drupalGet('quick/foo/configure');
|
||||
$this->drupalGet('setup/quick/foo/edit');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Go to the requires_entity_test quick form and confirm 404 not found.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace Drupal\Tests\farm_quick\Kernel;
|
||||
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\farm_quick\Form\ConfigureQuickForm;
|
||||
use Drupal\farm_quick\Form\QuickFormEntityForm;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
|
@ -152,11 +152,15 @@ class QuickFormTest extends KernelTestBase {
|
|||
// Confirm that the config entity for this quick form has not been saved.
|
||||
$this->assertTrue($quick_form->isNew());
|
||||
|
||||
// Programmatically submit the configurable_test config form.
|
||||
$form = ConfigureQuickForm::create(\Drupal::getContainer());
|
||||
// Programmatically submit the quick form entity form.
|
||||
$form = QuickFormEntityForm::create(\Drupal::getContainer());
|
||||
$form->setModuleHandler(\Drupal::moduleHandler());
|
||||
$form->setEntity($quick_form);
|
||||
$form_state = (new FormState())->setValues([
|
||||
// Set the ID and label because no default value is provided for these
|
||||
// in the form unless the override query param is set.
|
||||
'id' => $quick_form->id(),
|
||||
'label' => (string) $quick_form->label(),
|
||||
'settings' => [
|
||||
'test_default' => '101',
|
||||
],
|
||||
|
|
|
@ -11,10 +11,3 @@ farm.settings:
|
|||
parent: farm.setup
|
||||
route_name: farm_settings.settings_page
|
||||
weight: 100
|
||||
|
||||
farm.settings.farm_info:
|
||||
title: Farm Info
|
||||
description: Test
|
||||
parent: farm.settings
|
||||
route_name: farm_settings.settings_page
|
||||
weight: -10
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- farm_ui_location
|
||||
id: locations
|
||||
label: Locations
|
||||
description: 'The farmOS locations map.'
|
||||
behaviors:
|
||||
- locations
|
||||
options: { }
|
|
@ -6,5 +6,6 @@ core_version_requirement: ^10
|
|||
dependencies:
|
||||
- farm:farm_entity
|
||||
- farm:farm_location
|
||||
- farm:farm_ui_map
|
||||
- farm:farm_ui_menu
|
||||
- inspire_tree:inspire_tree
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Post update functions for farm_ui_location module.
|
||||
*/
|
||||
|
||||
use Drupal\farm_map\Entity\MapType;
|
||||
|
||||
/**
|
||||
* Add farmOS locations map type.
|
||||
*/
|
||||
function farm_ui_location_post_update_add_locations_map_type(&$sandbox = NULL) {
|
||||
|
||||
// Create locations map type.
|
||||
$map_type = MapType::create([
|
||||
'id' => 'locations',
|
||||
'label' => 'Locations',
|
||||
'description' => 'The farmOS locations map.',
|
||||
'behaviors' => [
|
||||
'location',
|
||||
],
|
||||
'options' => [],
|
||||
'dependencies' => [
|
||||
'enforced' => [
|
||||
'module' => [
|
||||
'farm_ui_location',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$map_type->save();
|
||||
}
|
|
@ -12,7 +12,6 @@ farm.asset.locations:
|
|||
_title_callback: '\Drupal\farm_ui_location\Form\LocationHierarchyForm::getTitle'
|
||||
_form: '\Drupal\farm_ui_location\Form\LocationHierarchyForm'
|
||||
requirements:
|
||||
_entity_access: 'asset.edit'
|
||||
_custom_access: '\Drupal\farm_ui_location\Form\LocationHierarchyForm::access'
|
||||
_module_dependencies: 'asset'
|
||||
asset: \d+
|
||||
|
|
|
@ -82,8 +82,13 @@ class LocationHierarchyForm extends FormBase {
|
|||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
// If the asset does not have child locations, forbid access.
|
||||
if (empty($this->getLocations($asset))) {
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
// Allow access if the asset has child locations.
|
||||
return AccessResult::allowedIf(!empty($this->getLocations($asset)));
|
||||
return AccessResult::allowedIf($asset->access('view', $account));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,6 +112,14 @@ class LocationHierarchyForm extends FormBase {
|
|||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, AssetInterface $asset = NULL) {
|
||||
|
||||
// If no asset was specified, show a map of all locations.
|
||||
if (is_null($asset)) {
|
||||
$form['map'] = [
|
||||
'#type' => 'farm_map',
|
||||
'#map_type' => 'locations',
|
||||
];
|
||||
}
|
||||
|
||||
// Add a DIV for the JavaScript content.
|
||||
$form['content'] = [
|
||||
'#type' => 'html_tag',
|
||||
|
@ -238,6 +251,11 @@ class LocationHierarchyForm extends FormBase {
|
|||
/** @var \Drupal\asset\Entity\AssetInterface[] $assets */
|
||||
$assets = $storage->loadMultiple($asset_ids);
|
||||
|
||||
// Filter out assets that the user cannot view.
|
||||
$assets = array_filter($assets, function ($asset) {
|
||||
return $asset->access('view');
|
||||
});
|
||||
|
||||
// Sort assets by name, using natural sort algorithm.
|
||||
usort($assets, function ($a, $b) {
|
||||
return strnatcmp($a->label(), $b->label());
|
||||
|
@ -269,12 +287,22 @@ class LocationHierarchyForm extends FormBase {
|
|||
// Maintain a list of assets that need to be saved.
|
||||
$save_assets = [];
|
||||
|
||||
// Maintain a list of assets that were not editable by the user.
|
||||
$restricted_assets = [];
|
||||
|
||||
// Iterate through the changes.
|
||||
foreach ($changes as $change) {
|
||||
|
||||
// Load the asset.
|
||||
$asset = $storage->load($change['asset_id']);
|
||||
|
||||
// If the user does not have permission to edit the asset, count it so
|
||||
// that we can add a warning message later, and skip it.
|
||||
if (!$asset->access('edit')) {
|
||||
$restricted_assets[] = $asset;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the original parent.
|
||||
if (!empty($asset->get('parent'))) {
|
||||
/** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $parent */
|
||||
|
@ -317,6 +345,12 @@ class LocationHierarchyForm extends FormBase {
|
|||
// Show a summary of the results.
|
||||
$message = $this->formatPlural(count($save_assets), 'Updated the parent hierarchy of %count asset.', 'Updated the parent hierarchy of %count assets.', ['%count' => count($save_assets)]);
|
||||
$this->messenger()->addStatus($message);
|
||||
|
||||
// If any edits were restricted, show a warning.
|
||||
if ($restricted_assets) {
|
||||
$message = $this->formatPlural(count($restricted_assets), '%count asset could not be changed because you do not have permission.', '%count assets could not be changed because you do not have permission.', ['%count' => count($restricted_assets)]);
|
||||
$this->messenger()->addWarning($message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- farm_ui_map
|
||||
module:
|
||||
- asset
|
||||
id: asset.map_popup
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- farm_ui_map
|
||||
id: locations
|
||||
label: Location asset layers
|
||||
description: 'Displays location asset geometries in layers by asset type.'
|
||||
library: ''
|
||||
settings: { }
|
|
@ -7,5 +7,6 @@ dependencies:
|
|||
id: dashboard
|
||||
label: Dashboard
|
||||
description: 'The farmOS dashboard map.'
|
||||
behaviors: { }
|
||||
behaviors:
|
||||
- locations
|
||||
options: { }
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue